From 70d98deff4e8874d720a68a4ad46571d82344238 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 16 Jun 2023 21:48:48 +0200 Subject: [PATCH] commit melonHLE it's a mess don't look inside --- src/CMakeLists.txt | 6 + src/DSi_SPI_TSC.cpp | 3 + src/GPU.cpp | 3 + src/GPU3D.cpp | 2 +- src/HLE.cpp | 54 + src/HLE.h | 36 + src/HLE_Retail/IPC.cpp | 750 ++++++++ src/HLE_Retail/IPC.h | 45 + src/HLE_Retail/Sound_Nitro.cpp | 2630 ++++++++++++++++++++++++++++ src/HLE_Retail/Sound_Nitro.h | 42 + src/HLE_Retail/Sound_Peach.cpp | 1887 ++++++++++++++++++++ src/HLE_Retail/Sound_Peach.h | 41 + src/HLE_Retail/Wifi.cpp | 453 +++++ src/HLE_Retail/Wifi.h | 41 + src/NDS.cpp | 37 +- src/NDS.h | 13 + src/NDSCart.cpp | 27 +- src/NDSCart.h | 6 +- src/SPI.cpp | 3 + src/Savestate.cpp | 425 ++--- src/Savestate.h | 78 +- src/frontend/qt_sdl/ROMManager.cpp | 127 +- src/frontend/qt_sdl/ROMManager.h | 1 - src/frontend/qt_sdl/main.cpp | 6 +- 24 files changed, 6255 insertions(+), 461 deletions(-) create mode 100644 src/HLE.cpp create mode 100644 src/HLE.h create mode 100644 src/HLE_Retail/IPC.cpp create mode 100644 src/HLE_Retail/IPC.h create mode 100644 src/HLE_Retail/Sound_Nitro.cpp create mode 100644 src/HLE_Retail/Sound_Nitro.h create mode 100644 src/HLE_Retail/Sound_Peach.cpp create mode 100644 src/HLE_Retail/Sound_Peach.h create mode 100644 src/HLE_Retail/Wifi.cpp create mode 100644 src/HLE_Retail/Wifi.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f915ade..be7b15d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,12 @@ add_library(core STATIC version.h Wifi.cpp WifiAP.cpp + + HLE.cpp + HLE_Retail/IPC.cpp + HLE_Retail/Sound_Nitro.cpp + HLE_Retail/Sound_Peach.cpp + HLE_Retail/Wifi.cpp fatfs/diskio.c fatfs/ff.c diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index c690f9c9..10077601 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -57,6 +57,9 @@ void Reset() Index = 0; Data = 0; + TouchX = 0; + TouchY = 0xFFF; + memset(Bank3Regs, 0, 0x80); Bank3Regs[0x02] = 0x18; Bank3Regs[0x03] = 0x87; diff --git a/src/GPU.cpp b/src/GPU.cpp index f51748b6..fd60d0ad 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -19,6 +19,7 @@ #include #include "NDS.h" #include "GPU.h" +#include "HLE.h" #ifdef JIT_ENABLED #include "ARMJIT.h" @@ -1186,6 +1187,8 @@ void StartScanline(u32 line) } } + HLE::StartScanline(line); + NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line); } diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 145b689f..5c485750 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -562,7 +562,7 @@ void DoSavestate(Savestate* file) file->Bool32(&poly->IsShadowMask); file->Bool32(&poly->IsShadow); - if (file->IsAtLeastVersion(4, 1)) + if (file->IsAtleastVersion(4, 1)) file->Var32((u32*)&poly->Type); else poly->Type = 0; diff --git a/src/HLE.cpp b/src/HLE.cpp new file mode 100644 index 00000000..241a26db --- /dev/null +++ b/src/HLE.cpp @@ -0,0 +1,54 @@ +/* + Copyright 2016-2022 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 "NDS.h" +#include "HLE.h" + +#include "HLE_Retail/IPC.h" + + +namespace HLE +{ + +void Reset() +{ + Retail::Reset(); +} + + +void StartScanline(u32 line) +{ + return Retail::StartScanline(line); +} + + +void OnIPCSync() +{ + // TODO: select retail or homebrew HLE + + return Retail::OnIPCSync(); +} + +void OnIPCRequest() +{ + // TODO: select retail or homebrew HLE + + return Retail::OnIPCRequest(); +} + +} diff --git a/src/HLE.h b/src/HLE.h new file mode 100644 index 00000000..feb7e25d --- /dev/null +++ b/src/HLE.h @@ -0,0 +1,36 @@ +/* + Copyright 2016-2022 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 HLE_H +#define HLE_H + +#include "types.h" + +namespace HLE +{ + +void Reset(); + +void StartScanline(u32 line); + +void OnIPCSync(); +void OnIPCRequest(); + +} + +#endif // HLE_H diff --git a/src/HLE_Retail/IPC.cpp b/src/HLE_Retail/IPC.cpp new file mode 100644 index 00000000..e2c43456 --- /dev/null +++ b/src/HLE_Retail/IPC.cpp @@ -0,0 +1,750 @@ +/* + Copyright 2016-2022 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 "../NDS.h" +#include "../NDSCart.h" +#include "../HLE.h" +#include "../FIFO.h" +#include "../SPU.h" + +#include "IPC.h" +#include "Sound_Nitro.h" +#include "Sound_Peach.h" +#include "Wifi.h" + + +namespace NDS +{ +extern u16 IPCSync9, IPCSync7; +extern u16 IPCFIFOCnt9, IPCFIFOCnt7; +extern FIFO IPCFIFO9; // FIFO in which the ARM9 writes +extern FIFO IPCFIFO7; +} + +namespace SPI_Firmware +{ +extern u8* Firmware; +extern u32 FirmwareMask; +u8 Read(); +void Write(u8 val, u32 hold); +} + +namespace SPI_Powerman +{ +extern u8 Registers[8]; +extern u8 RegMasks[8]; +u8 Read(); +void Write(u8 val, u32 hold); +} + +namespace SPI_TSC +{ +extern u16 TouchX, TouchY; +} + +namespace RTC +{ +extern u32 InputPos; +extern u8 Output[8]; +void ByteIn(u8 val); +} + + +namespace HLE +{ +namespace Retail +{ + +u16 FW_Data[16]; + +bool TS_Inited; +u16 TS_Data[16]; +u16 TS_NumSamples; +u16 TS_SamplePos[4]; + +int Sound_Engine; + +u16 PM_Data[16]; + +u16 Mic_Data[16]; + +u32 SM_Command; +u32 SM_DataPos; +u32 SM_Buffer; + + +void SendIPCSync(u8 val) +{ + NDS::IPCSync9 = (NDS::IPCSync9 & 0xFFF0) | (val & 0xF); +} + +void SendIPCReply(u32 service, u32 data, u32 flag) +{ + u32 val = (service & 0x1F) | (data << 6) | ((flag & 0x1) << 5); + + if (NDS::IPCFIFO7.IsFull()) + printf("!!!! IPC FIFO FULL\n"); + else + { + bool wasempty = NDS::IPCFIFO7.IsEmpty(); + NDS::IPCFIFO7.Write(val); + if ((NDS::IPCFIFOCnt9 & 0x0400) && wasempty) + NDS::SetIRQ(0, NDS::IRQ_IPCRecv); + } +} + + +void Reset() +{ + memset(FW_Data, 0, sizeof(FW_Data)); + + TS_Inited = false; + memset(TS_Data, 0, sizeof(TS_Data)); + TS_NumSamples = 0; + memset(TS_SamplePos, 0, sizeof(TS_SamplePos)); + + Sound_Engine = -1; + + memset(PM_Data, 0, sizeof(PM_Data)); + + memset(Mic_Data, 0, sizeof(Mic_Data)); + + SM_Command = 0; + SM_DataPos = 0; + SM_Buffer = 0; + + Wifi::Reset(); + + NDS::ScheduleEvent(NDS::Event_HLE_PollInput, true, 134016, PollInput, 0); +} + + +void Touchscreen_Sample() +{ + u32 ts = NDS::ARM7Read16(0x027FFFAA) | (NDS::ARM7Read16(0x027FFFAC) << 16); + + if (SPI_TSC::TouchY == 0xFFF) + { + ts &= 0xFE000000; + ts |= 0x06000000; + } + else + { + ts &= 0xF9000000; + ts |= (SPI_TSC::TouchX & 0xFFF); + ts |= ((SPI_TSC::TouchY & 0xFFF) << 12); + ts |= 0x01000000; + } + + NDS::ARM7Write16(0x027FFFAA, ts & 0xFFFF); + NDS::ARM7Write16(0x027FFFAC, ts >> 16); +} + +void StartScanline(u32 line) +{ + for (int i = 0; i < TS_NumSamples; i++) + { + if (line == TS_SamplePos[i]) + { + Touchscreen_Sample(); + SendIPCReply(0x6, 0x03009000 | i); + break; + } + } +} + +void PollInput(u32 param) +{ + u16 input = (NDS::KeyInput >> 6) & 0x2C00; + if (NDS::IsLidClosed()) input |= (1<<15); + + NDS::ARM7Write16(0x027FFFA8, input); + + NDS::ScheduleEvent(NDS::Event_HLE_PollInput, true, 134016, PollInput, 0); +} + + +void OnIPCSync() +{ + u8 val = NDS::IPCSync7 & 0xF; + + if (val < 5) + { + SendIPCSync(val+1); + } + else if (val == 5) + { + SendIPCSync(0); + + // presumably ARM7-side ready flags for each IPC service + NDS::ARM7Write32(0x027FFF8C, 0x0000FFF0); + } +} + + +void OnIPCRequest_Firmware(u32 data) +{ + if (data & (1<<25)) + { + memset(FW_Data, 0, sizeof(FW_Data)); + } + + FW_Data[(data >> 16) & 0xF] = data & 0xFFFF; + + if (!(data & (1<<24))) return; + + u32 cmd = (FW_Data[0] >> 8) - 0x20; + switch (cmd) + { + case 0: // write enable + SPI_Firmware::Write(0x06, false); + SendIPCReply(0x4, 0x0300A000); + break; + + case 1: // write disable + SPI_Firmware::Write(0x04, false); + SendIPCReply(0x4, 0x0300A100); + break; + + case 2: // read status register + { + u32 addr = ((FW_Data[0] & 0xFF) << 24) | (FW_Data[1] << 8) | ((FW_Data[2] >> 8) & 0xFF); + if (addr < 0x02000000 || addr >= 0x02800000) + { + SendIPCReply(0x4, 0x0300A202); + break; + } + + SPI_Firmware::Write(0x05, true); + SPI_Firmware::Write(0, false); + u8 ret = SPI_Firmware::Read(); + NDS::ARM7Write8(addr, ret); + + SendIPCReply(0x4, 0x0300A200); + } + break; + + case 3: // firmware read + { + u32 addr = (FW_Data[4] << 16) | FW_Data[5]; + if (addr < 0x02000000 || addr >= 0x02800000) + { + SendIPCReply(0x4, 0x0300A302); + break; + } + + u32 src = ((FW_Data[0] & 0xFF) << 16) | FW_Data[1]; + u32 len = (FW_Data[2] << 16) | FW_Data[3]; + + for (u32 i = 0; i < len; i++) + { + u8 val = SPI_Firmware::Firmware[src & SPI_Firmware::FirmwareMask]; + NDS::ARM7Write8(addr, val); + src++; + addr++; + } + + SendIPCReply(0x4, 0x0300A300); + } + break; + + case 5: // firmware write + { + u32 addr = (FW_Data[3] << 16) | FW_Data[4]; + if (addr < 0x02000000 || addr >= 0x02800000) + { + SendIPCReply(0x4, 0x0300A502); + break; + } + + u32 dst = ((FW_Data[0] & 0xFF) << 16) | FW_Data[1]; + u32 len = FW_Data[2]; + + for (u32 i = 0; i < len; i++) + { + u8 val = NDS::ARM7Read8(addr); + SPI_Firmware::Firmware[dst & SPI_Firmware::FirmwareMask] = val; + dst++; + addr++; + } + + // hack: trigger firmware save + SPI_Firmware::Write(0x0A, true); + SPI_Firmware::Write(0, false); + + SendIPCReply(0x4, 0x0300A500); + } + break; + + default: + printf("unknown FW request %08X (%04X)\n", data, FW_Data[0]); + break; + } +} + +void RTC_Read(u8 reg, u32 addr, u32 len) +{ + RTC::InputPos = 0; + RTC::ByteIn(reg | 0x80); + + for (u32 i = 0; i < len; i++) + { + NDS::ARM7Write8(addr+i, RTC::Output[i]); + } +} + +void OnIPCRequest_RTC(u32 data) +{ + u32 cmd = (data >> 8) & 0x7F; + + if ((cmd >= 2 && cmd <= 15) || + (cmd >= 26 && cmd <= 34) || + (cmd >= 42)) + { + SendIPCReply(0x5, 0x8001 | (cmd << 8)); + return; + } + + switch (cmd) + { + case 0x11: // read date + RTC_Read(0x20, 0x027FFDE8, 4); + SendIPCReply(0x5, 0x9100); + break; + + case 0x12: // read time + RTC_Read(0x60, 0x027FFDE8+4, 3); + SendIPCReply(0x5, 0x9200); + break; + + default: + printf("HLE: unknown RTC command %02X (%08X)\n", cmd, data); + break; + } +} + +void OnIPCRequest_Touchscreen(u32 data) +{ + if (data & (1<<25)) + { + memset(TS_Data, 0, sizeof(TS_Data)); + } + + TS_Data[(data >> 16) & 0xF] = data & 0xFFFF; + + if (!(data & (1<<24))) return; + + switch (TS_Data[0] >> 8) + { + case 0: // manual sampling + { + Touchscreen_Sample(); + SendIPCReply(0x6, 0x03008000); + } + break; + + case 1: // setup auto sampling + { + if (TS_Inited) + { + SendIPCReply(0x6, 0x03008103); + break; + } + + // samples per frame + u8 num = TS_Data[0] & 0xFF; + if (num == 0 || num > 4) + { + SendIPCReply(0x6, 0x03008102); + break; + } + + // offset in scanlines for first sample + u16 offset = TS_Data[1]; + if (offset >= 263) + { + SendIPCReply(0x6, 0x03008102); + break; + } + + TS_NumSamples = num; + for (int i = 0; i < num; i++) + { + u32 ypos = (offset + ((i * 263) / num)) % 263; + TS_SamplePos[i] = ypos; + } + + TS_Inited = true; + SendIPCReply(0x6, 0x03008100); + } + break; + + case 2: // stop autosampling + { + // TODO CHECKME + // Mario Kart uses this + // but this here is wrong + TS_NumSamples = 0; + SendIPCReply(0x6, 0x03008200); + } + break; + + case 3: // manual sampling but with condition (TODO) + { + Touchscreen_Sample(); + SendIPCReply(0x6, 0x03008300); + } + break; + + default: + printf("unknown TS request %08X\n", data); + break; + } +} + +void OnIPCRequest_Sound(u32 data) +{ + if (Sound_Engine == -1) + { + if (data >= 0x02000000) + { + Sound_Engine = 0; + Sound_Nitro::Reset(); + } + else + { + Sound_Engine = 1; + Sound_Peach::Reset(); + } + } + + if (Sound_Engine == 0) return Sound_Nitro::OnIPCRequest(data); + if (Sound_Engine == 1) return Sound_Peach::OnIPCRequest(data); +} + +void OnIPCRequest_Powerman(u32 data) +{ + if (data & (1<<25)) + { + memset(PM_Data, 0, sizeof(PM_Data)); + } + + PM_Data[(data >> 16) & 0xF] = data & 0xFFFF; + + if (!(data & (1<<24))) return; + + u32 cmd = (PM_Data[0] >> 8) - 0x60; + printf("PM CMD %04X %04X\n", PM_Data[0], PM_Data[1]); + switch (cmd) + { + case 3: // utility + { + switch (PM_Data[1] & 0xFF) + { + case 1: // power LED: steady + SPI_Powerman::Registers[0] &= ~0x10; + break; + case 2: // power LED: fast blink + SPI_Powerman::Registers[0] |= 0x30; + break; + case 3: // power LED: slow blink + SPI_Powerman::Registers[0] &= ~0x20; + SPI_Powerman::Registers[0] |= 0x10; + break; + case 4: // lower backlights on + SPI_Powerman::Registers[0] |= 0x04; + break; + case 5: // lower backlights off + SPI_Powerman::Registers[0] &= ~0x04; + break; + case 6: // upper backlights on + SPI_Powerman::Registers[0] |= 0x08; + break; + case 7: // upper backlights off + SPI_Powerman::Registers[0] &= ~0x08; + break; + case 8: // backlights on + SPI_Powerman::Registers[0] |= 0x0C; + break; + case 9: // backlights off + SPI_Powerman::Registers[0] &= ~0x0C; + break; + case 10: // sound amp on + SPI_Powerman::Registers[0] |= 0x01; + break; + case 11: // sound amp off + SPI_Powerman::Registers[0] &= ~0x01; + break; + case 12: // sound mute on + SPI_Powerman::Registers[0] |= 0x02; + break; + case 13: // sound mute off + SPI_Powerman::Registers[0] &= ~0x02; + break; + case 14: // shutdown + SPI_Powerman::Registers[0] &= ~0x01; + SPI_Powerman::Registers[0] |= 0x40; + NDS::Stop(); + break; + case 15: // ???? + SPI_Powerman::Registers[0] &= ~0x40; + break; + } + + SendIPCReply(0x8, 0x0300E300); + } + break; + + case 4: // write register + { + u8 addr = PM_Data[0] & 0xFF; + u8 val = PM_Data[1] & 0xFF; + SPI_Powerman::Write(addr & 0x7F, true); + SPI_Powerman::Write(val, false); + SendIPCReply(0x8, 0x03008000 | (((PM_Data[1] + 0x70) & 0xFF) << 8)); + } + break; + + case 5: // read register + { + u8 addr = PM_Data[0] & 0xFF; + SPI_Powerman::Write((addr & 0x7F) | 0x80, true); + SPI_Powerman::Write(0, false); + u8 ret = SPI_Powerman::Read(); + SendIPCReply(0x8, 0x03008000 | ret | (((PM_Data[1] + 0x70) & 0xFF) << 8)); + } + break; + + case 6: + { + // TODO + + SendIPCReply(0x8, 0x03008000 | (((PM_Data[1] + 0x70) & 0xFF) << 8)); + } + break; + } +} + +void OnIPCRequest_Mic(u32 data) +{ + if (data & (1<<25)) + { + memset(Mic_Data, 0, sizeof(Mic_Data)); + } + + Mic_Data[(data >> 16) & 0xF] = data & 0xFFFF; + + if (!(data & (1<<24))) return; + + u32 cmd = (Mic_Data[0] >> 8) - 0x40; + switch (cmd) + { + case 0: // sampling? + { + // TODO + + SendIPCReply(0x9, 0x0300C000); + } + break; + + default: + printf("unknown mic request %08X\n", data); + break; + } +} + +void OnIPCRequest_CartSave(u32 data) +{ + if (SM_DataPos == 0) + SM_Command = data; + + switch (SM_Command) + { + case 0: + if (SM_DataPos == 0) break; + if (SM_DataPos == 1) + { + SM_Buffer = data; + SendIPCReply(0xB, 0x1, 1); + SM_DataPos = 0; + return; + } + break; + + case 2: // identify savemem + // TODO + SendIPCReply(0xB, 0x1, 1); + SM_DataPos = 0; + return; + + case 6: // read + { + u32 offset = NDS::ARM7Read32(SM_Buffer+0x0C); + u32 dst = NDS::ARM7Read32(SM_Buffer+0x10); + u32 len = NDS::ARM7Read32(SM_Buffer+0x14); + + u8* mem = NDSCart::GetSaveMemory(); + u32 memlen = NDSCart::GetSaveMemoryLength(); + if (mem && memlen) + { + memlen--; + + for (u32 i = 0; i < len; i++) + { + NDS::ARM7Write8(dst, mem[offset & memlen]); + dst++; + offset++; + } + } + + SendIPCReply(0xB, 0x1, 1); + SM_DataPos = 0; + return; + } + break; + + case 8: // write + { + u32 src = NDS::ARM7Read32(SM_Buffer+0x0C); + u32 offset = NDS::ARM7Read32(SM_Buffer+0x10); + u32 len = NDS::ARM7Read32(SM_Buffer+0x14); + + printf("IPC CMD8: %08X %08X %08X\n", src, offset, len); + + SendIPCReply(0xB, 0x1, 1); + SM_DataPos = 0; + return; + } + break; + + case 9: // verify + { + u32 src = NDS::ARM7Read32(SM_Buffer+0x0C); + u32 offset = NDS::ARM7Read32(SM_Buffer+0x10); + u32 len = NDS::ARM7Read32(SM_Buffer+0x14); + + printf("IPC CMD9: %08X %08X %08X\n", src, offset, len); + + // writes result to first word of IPC buffer + + SendIPCReply(0xB, 0x1, 1); + SM_DataPos = 0; + return; + } + break; + + default: + printf("SAVEMEM: unknown cmd %08X\n", SM_Command); + break; + } + + SM_DataPos++; +} + +void OnIPCRequest_Cart(u32 data) +{ + if ((data & 0x3F) == 1) + { + // TODO other shito? + + SendIPCReply(0xD, 0x1); + } + else + { + // do something else + } + /*if (data & 0x1) + { + // init + + SendIPCReply(0xD, 0x1); + }*/ +} + + +void OnIPCRequest() +{ + u32 val = NDS::IPCFIFO9.Read(); + + if (NDS::IPCFIFO9.IsEmpty() && (NDS::IPCFIFOCnt9 & 0x0004)) + NDS::SetIRQ(0, NDS::IRQ_IPCSendDone); + + u32 service = val & 0x1F; + u32 data = val >> 6; + u32 flag = (val >> 5) & 0x1; +//printf("IPC %08X\n", val); + switch (service) + { + case 0x4: // firmware + if (flag) break; + OnIPCRequest_Firmware(data); + break; + + case 0x5: // RTC + if (flag) break; + OnIPCRequest_RTC(data); + break; + + case 0x6: // touchscreen + if (flag) break; + OnIPCRequest_Touchscreen(data); + break; + + case 0x7: // sound + OnIPCRequest_Sound(data); + break; + + case 0x8: // powerman + if (flag) break; + OnIPCRequest_Powerman(data); + break; + + case 0x9: // mic + if (flag) break; + OnIPCRequest_Mic(data); + break; + + case 0xA: // wifi + if (flag) break; + Wifi::OnIPCRequest(data); + break; + + case 0xB: // cart savemem + if (!flag) break; + OnIPCRequest_CartSave(data); + break; + + case 0xD: // cart + OnIPCRequest_Cart(data); + break; + + case 0xF: + if (data == 0x10000) + { + SendIPCReply(0xF, 0x10000); + } + break; + + default: + printf("HLE: unknown IPC request %08X service=%02X data=%08X flag=%d\n", val, service, data, flag); + break; + } +} + +} +} diff --git a/src/HLE_Retail/IPC.h b/src/HLE_Retail/IPC.h new file mode 100644 index 00000000..29352937 --- /dev/null +++ b/src/HLE_Retail/IPC.h @@ -0,0 +1,45 @@ +/* + Copyright 2016-2022 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 HLE_RETAIL_IPC_H +#define HLE_RETAIL_IPC_H + +#include "../types.h" + +namespace HLE +{ +namespace Retail +{ + +void Reset(); + +void SendIPCReply(u32 service, u32 data, u32 flag = 0); + +void StartScanline(u32 line); +void PollInput(u32 param); +void SoundProcess(u32 param); +void SoundPeach(u32 param); +void SoundAlarm(u32 num); + +void OnIPCSync(); +void OnIPCRequest(); + +} +} + +#endif // HLE_RETAIL_IPC_H diff --git a/src/HLE_Retail/Sound_Nitro.cpp b/src/HLE_Retail/Sound_Nitro.cpp new file mode 100644 index 00000000..2e07d633 --- /dev/null +++ b/src/HLE_Retail/Sound_Nitro.cpp @@ -0,0 +1,2630 @@ +/* + Copyright 2016-2022 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 "../NDS.h" +#include "../NDSCart.h" +#include "../HLE.h" +#include "../SPU.h" +#include "../FIFO.h" + +#include "IPC.h" +#include "Sound_Nitro.h" + + +namespace HLE +{ +namespace Retail +{ +namespace Sound_Nitro +{ + +struct Track; + +struct Channel +{ + u8 ID; + u8 Type; + u8 VolRampPhase; + u8 StatusFlags; + u8 PanBase1; + u8 FreqBase1; + s16 VolBase1; + u8 FreqBase2; + u8 VolBase2; + s8 PanBase2; + s8 PanBase3; + s16 VolBase3; + s16 FreqBase3; + s32 BaseVolume; + s32 FreqRampPos; + s32 FreqRampLen; + u8 AttackRate; + u8 SustainRate; + u16 DecayRate; + u16 ReleaseRate; + u8 Priority; + u8 Pan; + u8 Volume; + u8 VolumeDiv; + u16 Frequency; + u8 ModulationType; + u8 ModulationSpeed; + u8 ModulationDepth; + u8 ModulationRange; + u16 ModulationDelay; + u16 ModulationCount1; + u16 ModulationCount2; + s16 FreqRampTarget; + s32 NoteLength; + u8 DataFormat; + u8 Repeat; + u16 SampleRate; + u16 SwavFrequency; + u16 LoopPos; + u32 Length; + u32 DataAddr; + bool Linked; + Track* LinkedTrack; + Channel* Next; +}; + +struct Sequence +{ + u8 StatusFlags; + u8 ID; + u8 SeqUnk02; + u8 SeqUnk03; + u8 Pan; + u8 Volume; + s16 SeqUnk06; + u8 Tracks[16]; + u16 Tempo; + u16 SeqUnk1A; + u16 TickCounter; + u32 SBNKAddr; +}; + +struct Track +{ + u8 StatusFlags; + u8 TrackUnk01; + u16 InstrIndex; // index into SBNK + u8 Volume; + u8 Expression; + s8 PitchBend; + u8 PitchBendRange; + s8 Pan; + s8 TrackUnk09; + s16 TrackUnk0A; + s16 Frequency; + u8 AttackRate; + u8 DecayRate; + u8 SustainRate; + u8 ReleaseRate; + u8 Priority; + s8 Transpose; + s8 TrackUnk14; + u8 PortamentoTime; + s16 SweepPitch; + u8 ModulationType; + u8 ModulationSpeed; + u8 ModulationDepth; + u8 ModulationRange; + u16 ModulationDelay; + u16 ChannelMask; + s32 RestCounter; + u32 NoteBuffer; + u32 CurNoteAddr; + u32 LoopAddr[3]; + u8 LoopCount[3]; + u8 LoopLevel; + Channel* ChanList; +}; + +struct Alarm +{ + bool Active; + u32 Delay; + u32 Repeat; + u32 Param; +}; + +const s16 BaseVolumeTable[128] = +{ + -0x8000, -0x02D2, -0x02D1, -0x028B, -0x0259, -0x0232, -0x0212, -0x01F7, + -0x01E0, -0x01CC, -0x01BA, -0x01A9, -0x019A, -0x018C, -0x017F, -0x0173, + -0x0168, -0x015D, -0x0153, -0x014A, -0x0141, -0x0139, -0x0131, -0x0129, + -0x0121, -0x011A, -0x0114, -0x010D, -0x0107, -0x0101, -0x00FB, -0x00F5, + -0x00EF, -0x00EA, -0x00E5, -0x00E0, -0x00DB, -0x00D6, -0x00D2, -0x00CD, + -0x00C9, -0x00C4, -0x00C0, -0x00BC, -0x00B8, -0x00B4, -0x00B0, -0x00AD, + -0x00A9, -0x00A5, -0x00A2, -0x009E, -0x009B, -0x0098, -0x0095, -0x0091, + -0x008E, -0x008B, -0x0088, -0x0085, -0x0082, -0x007F, -0x007D, -0x007A, + -0x0077, -0x0074, -0x0072, -0x006F, -0x006D, -0x006A, -0x0067, -0x0065, + -0x0063, -0x0060, -0x005E, -0x005B, -0x0059, -0x0057, -0x0055, -0x0052, + -0x0050, -0x004E, -0x004C, -0x004A, -0x0048, -0x0046, -0x0044, -0x0042, + -0x0040, -0x003E, -0x003C, -0x003A, -0x0038, -0x0036, -0x0034, -0x0032, + -0x0031, -0x002F, -0x002D, -0x002B, -0x002A, -0x0028, -0x0026, -0x0024, + -0x0023, -0x0021, -0x001F, -0x001E, -0x001C, -0x001B, -0x0019, -0x0017, + -0x0016, -0x0014, -0x0013, -0x0011, -0x0010, -0x000E, -0x000D, -0x000B, + -0x000A, -0x0008, -0x0007, -0x0006, -0x0004, -0x0003, -0x0001, 0x0000 +}; + +const s8 ModulationTable[32] = +{ + 0x00, 0x06, 0x0C, 0x13, 0x19, 0x1F, 0x25, 0x2B, 0x31, 0x36, 0x3C, 0x41, 0x47, 0x4C, 0x51, 0x55, + 0x5A, 0x5E, 0x62, 0x66, 0x6A, 0x6D, 0x70, 0x73, 0x75, 0x78, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0x7F +}; + +const u8 SWIVolumeTable[724] = +{ + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, + 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C, 0x1C, 0x1C, + 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29, + 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x31, + 0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3A, 0x3A, 0x3B, + 0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x3F, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7A, 0x7B, + 0x7D, 0x7E, 0x7F, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, + 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, 0x2D, + 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, + 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3B, 0x3C, 0x3D, 0x3E, 0x3E, 0x3F, 0x40, 0x40, + 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4D, + 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, + 0x5E, 0x5F, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6F, 0x70, + 0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7B, 0x7C, 0x7E, 0x7E, 0x40, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, + 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x71, 0x72, 0x74, 0x75, + 0x76, 0x78, 0x79, 0x7B, 0x7C, 0x7D, 0x7E, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, + 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6C, 0x6D, 0x6E, 0x6F, 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7A, + 0x7C, 0x7D, 0x7E, 0x7F +}; + +const u16 SWIPitchTable[768] = +{ + 0x0000, 0x003B, 0x0076, 0x00B2, 0x00ED, 0x0128, 0x0164, 0x019F, + 0x01DB, 0x0217, 0x0252, 0x028E, 0x02CA, 0x0305, 0x0341, 0x037D, + 0x03B9, 0x03F5, 0x0431, 0x046E, 0x04AA, 0x04E6, 0x0522, 0x055F, + 0x059B, 0x05D8, 0x0614, 0x0651, 0x068D, 0x06CA, 0x0707, 0x0743, + 0x0780, 0x07BD, 0x07FA, 0x0837, 0x0874, 0x08B1, 0x08EF, 0x092C, + 0x0969, 0x09A7, 0x09E4, 0x0A21, 0x0A5F, 0x0A9C, 0x0ADA, 0x0B18, + 0x0B56, 0x0B93, 0x0BD1, 0x0C0F, 0x0C4D, 0x0C8B, 0x0CC9, 0x0D07, + 0x0D45, 0x0D84, 0x0DC2, 0x0E00, 0x0E3F, 0x0E7D, 0x0EBC, 0x0EFA, + 0x0F39, 0x0F78, 0x0FB6, 0x0FF5, 0x1034, 0x1073, 0x10B2, 0x10F1, + 0x1130, 0x116F, 0x11AE, 0x11EE, 0x122D, 0x126C, 0x12AC, 0x12EB, + 0x132B, 0x136B, 0x13AA, 0x13EA, 0x142A, 0x146A, 0x14A9, 0x14E9, + 0x1529, 0x1569, 0x15AA, 0x15EA, 0x162A, 0x166A, 0x16AB, 0x16EB, + 0x172C, 0x176C, 0x17AD, 0x17ED, 0x182E, 0x186F, 0x18B0, 0x18F0, + 0x1931, 0x1972, 0x19B3, 0x19F5, 0x1A36, 0x1A77, 0x1AB8, 0x1AFA, + 0x1B3B, 0x1B7D, 0x1BBE, 0x1C00, 0x1C41, 0x1C83, 0x1CC5, 0x1D07, + 0x1D48, 0x1D8A, 0x1DCC, 0x1E0E, 0x1E51, 0x1E93, 0x1ED5, 0x1F17, + 0x1F5A, 0x1F9C, 0x1FDF, 0x2021, 0x2064, 0x20A6, 0x20E9, 0x212C, + 0x216F, 0x21B2, 0x21F5, 0x2238, 0x227B, 0x22BE, 0x2301, 0x2344, + 0x2388, 0x23CB, 0x240E, 0x2452, 0x2496, 0x24D9, 0x251D, 0x2561, + 0x25A4, 0x25E8, 0x262C, 0x2670, 0x26B4, 0x26F8, 0x273D, 0x2781, + 0x27C5, 0x280A, 0x284E, 0x2892, 0x28D7, 0x291C, 0x2960, 0x29A5, + 0x29EA, 0x2A2F, 0x2A74, 0x2AB9, 0x2AFE, 0x2B43, 0x2B88, 0x2BCD, + 0x2C13, 0x2C58, 0x2C9D, 0x2CE3, 0x2D28, 0x2D6E, 0x2DB4, 0x2DF9, + 0x2E3F, 0x2E85, 0x2ECB, 0x2F11, 0x2F57, 0x2F9D, 0x2FE3, 0x302A, + 0x3070, 0x30B6, 0x30FD, 0x3143, 0x318A, 0x31D0, 0x3217, 0x325E, + 0x32A5, 0x32EC, 0x3332, 0x3379, 0x33C1, 0x3408, 0x344F, 0x3496, + 0x34DD, 0x3525, 0x356C, 0x35B4, 0x35FB, 0x3643, 0x368B, 0x36D3, + 0x371A, 0x3762, 0x37AA, 0x37F2, 0x383A, 0x3883, 0x38CB, 0x3913, + 0x395C, 0x39A4, 0x39ED, 0x3A35, 0x3A7E, 0x3AC6, 0x3B0F, 0x3B58, + 0x3BA1, 0x3BEA, 0x3C33, 0x3C7C, 0x3CC5, 0x3D0E, 0x3D58, 0x3DA1, + 0x3DEA, 0x3E34, 0x3E7D, 0x3EC7, 0x3F11, 0x3F5A, 0x3FA4, 0x3FEE, + 0x4038, 0x4082, 0x40CC, 0x4116, 0x4161, 0x41AB, 0x41F5, 0x4240, + 0x428A, 0x42D5, 0x431F, 0x436A, 0x43B5, 0x4400, 0x444B, 0x4495, + 0x44E1, 0x452C, 0x4577, 0x45C2, 0x460D, 0x4659, 0x46A4, 0x46F0, + 0x473B, 0x4787, 0x47D3, 0x481E, 0x486A, 0x48B6, 0x4902, 0x494E, + 0x499A, 0x49E6, 0x4A33, 0x4A7F, 0x4ACB, 0x4B18, 0x4B64, 0x4BB1, + 0x4BFE, 0x4C4A, 0x4C97, 0x4CE4, 0x4D31, 0x4D7E, 0x4DCB, 0x4E18, + 0x4E66, 0x4EB3, 0x4F00, 0x4F4E, 0x4F9B, 0x4FE9, 0x5036, 0x5084, + 0x50D2, 0x5120, 0x516E, 0x51BC, 0x520A, 0x5258, 0x52A6, 0x52F4, + 0x5343, 0x5391, 0x53E0, 0x542E, 0x547D, 0x54CC, 0x551A, 0x5569, + 0x55B8, 0x5607, 0x5656, 0x56A5, 0x56F4, 0x5744, 0x5793, 0x57E2, + 0x5832, 0x5882, 0x58D1, 0x5921, 0x5971, 0x59C1, 0x5A10, 0x5A60, + 0x5AB0, 0x5B01, 0x5B51, 0x5BA1, 0x5BF1, 0x5C42, 0x5C92, 0x5CE3, + 0x5D34, 0x5D84, 0x5DD5, 0x5E26, 0x5E77, 0x5EC8, 0x5F19, 0x5F6A, + 0x5FBB, 0x600D, 0x605E, 0x60B0, 0x6101, 0x6153, 0x61A4, 0x61F6, + 0x6248, 0x629A, 0x62EC, 0x633E, 0x6390, 0x63E2, 0x6434, 0x6487, + 0x64D9, 0x652C, 0x657E, 0x65D1, 0x6624, 0x6676, 0x66C9, 0x671C, + 0x676F, 0x67C2, 0x6815, 0x6869, 0x68BC, 0x690F, 0x6963, 0x69B6, + 0x6A0A, 0x6A5E, 0x6AB1, 0x6B05, 0x6B59, 0x6BAD, 0x6C01, 0x6C55, + 0x6CAA, 0x6CFE, 0x6D52, 0x6DA7, 0x6DFB, 0x6E50, 0x6EA4, 0x6EF9, + 0x6F4E, 0x6FA3, 0x6FF8, 0x704D, 0x70A2, 0x70F7, 0x714D, 0x71A2, + 0x71F7, 0x724D, 0x72A2, 0x72F8, 0x734E, 0x73A4, 0x73FA, 0x7450, + 0x74A6, 0x74FC, 0x7552, 0x75A8, 0x75FF, 0x7655, 0x76AC, 0x7702, + 0x7759, 0x77B0, 0x7807, 0x785E, 0x78B4, 0x790C, 0x7963, 0x79BA, + 0x7A11, 0x7A69, 0x7AC0, 0x7B18, 0x7B6F, 0x7BC7, 0x7C1F, 0x7C77, + 0x7CCF, 0x7D27, 0x7D7F, 0x7DD7, 0x7E2F, 0x7E88, 0x7EE0, 0x7F38, + 0x7F91, 0x7FEA, 0x8042, 0x809B, 0x80F4, 0x814D, 0x81A6, 0x81FF, + 0x8259, 0x82B2, 0x830B, 0x8365, 0x83BE, 0x8418, 0x8472, 0x84CB, + 0x8525, 0x857F, 0x85D9, 0x8633, 0x868E, 0x86E8, 0x8742, 0x879D, + 0x87F7, 0x8852, 0x88AC, 0x8907, 0x8962, 0x89BD, 0x8A18, 0x8A73, + 0x8ACE, 0x8B2A, 0x8B85, 0x8BE0, 0x8C3C, 0x8C97, 0x8CF3, 0x8D4F, + 0x8DAB, 0x8E07, 0x8E63, 0x8EBF, 0x8F1B, 0x8F77, 0x8FD4, 0x9030, + 0x908C, 0x90E9, 0x9146, 0x91A2, 0x91FF, 0x925C, 0x92B9, 0x9316, + 0x9373, 0x93D1, 0x942E, 0x948C, 0x94E9, 0x9547, 0x95A4, 0x9602, + 0x9660, 0x96BE, 0x971C, 0x977A, 0x97D8, 0x9836, 0x9895, 0x98F3, + 0x9952, 0x99B0, 0x9A0F, 0x9A6E, 0x9ACD, 0x9B2C, 0x9B8B, 0x9BEA, + 0x9C49, 0x9CA8, 0x9D08, 0x9D67, 0x9DC7, 0x9E26, 0x9E86, 0x9EE6, + 0x9F46, 0x9FA6, 0xA006, 0xA066, 0xA0C6, 0xA127, 0xA187, 0xA1E8, + 0xA248, 0xA2A9, 0xA30A, 0xA36B, 0xA3CC, 0xA42D, 0xA48E, 0xA4EF, + 0xA550, 0xA5B2, 0xA613, 0xA675, 0xA6D6, 0xA738, 0xA79A, 0xA7FC, + 0xA85E, 0xA8C0, 0xA922, 0xA984, 0xA9E7, 0xAA49, 0xAAAC, 0xAB0E, + 0xAB71, 0xABD4, 0xAC37, 0xAC9A, 0xACFD, 0xAD60, 0xADC3, 0xAE27, + 0xAE8A, 0xAEED, 0xAF51, 0xAFB5, 0xB019, 0xB07C, 0xB0E0, 0xB145, + 0xB1A9, 0xB20D, 0xB271, 0xB2D6, 0xB33A, 0xB39F, 0xB403, 0xB468, + 0xB4CD, 0xB532, 0xB597, 0xB5FC, 0xB662, 0xB6C7, 0xB72C, 0xB792, + 0xB7F7, 0xB85D, 0xB8C3, 0xB929, 0xB98F, 0xB9F5, 0xBA5B, 0xBAC1, + 0xBB28, 0xBB8E, 0xBBF5, 0xBC5B, 0xBCC2, 0xBD29, 0xBD90, 0xBDF7, + 0xBE5E, 0xBEC5, 0xBF2C, 0xBF94, 0xBFFB, 0xC063, 0xC0CA, 0xC132, + 0xC19A, 0xC202, 0xC26A, 0xC2D2, 0xC33A, 0xC3A2, 0xC40B, 0xC473, + 0xC4DC, 0xC544, 0xC5AD, 0xC616, 0xC67F, 0xC6E8, 0xC751, 0xC7BB, + 0xC824, 0xC88D, 0xC8F7, 0xC960, 0xC9CA, 0xCA34, 0xCA9E, 0xCB08, + 0xCB72, 0xCBDC, 0xCC47, 0xCCB1, 0xCD1B, 0xCD86, 0xCDF1, 0xCE5B, + 0xCEC6, 0xCF31, 0xCF9C, 0xD008, 0xD073, 0xD0DE, 0xD14A, 0xD1B5, + 0xD221, 0xD28D, 0xD2F8, 0xD364, 0xD3D0, 0xD43D, 0xD4A9, 0xD515, + 0xD582, 0xD5EE, 0xD65B, 0xD6C7, 0xD734, 0xD7A1, 0xD80E, 0xD87B, + 0xD8E9, 0xD956, 0xD9C3, 0xDA31, 0xDA9E, 0xDB0C, 0xDB7A, 0xDBE8, + 0xDC56, 0xDCC4, 0xDD32, 0xDDA0, 0xDE0F, 0xDE7D, 0xDEEC, 0xDF5B, + 0xDFC9, 0xE038, 0xE0A7, 0xE116, 0xE186, 0xE1F5, 0xE264, 0xE2D4, + 0xE343, 0xE3B3, 0xE423, 0xE493, 0xE503, 0xE573, 0xE5E3, 0xE654, + 0xE6C4, 0xE735, 0xE7A5, 0xE816, 0xE887, 0xE8F8, 0xE969, 0xE9DA, + 0xEA4B, 0xEABC, 0xEB2E, 0xEB9F, 0xEC11, 0xEC83, 0xECF5, 0xED66, + 0xEDD9, 0xEE4B, 0xEEBD, 0xEF2F, 0xEFA2, 0xF014, 0xF087, 0xF0FA, + 0xF16D, 0xF1E0, 0xF253, 0xF2C6, 0xF339, 0xF3AD, 0xF420, 0xF494, + 0xF507, 0xF57B, 0xF5EF, 0xF663, 0xF6D7, 0xF74C, 0xF7C0, 0xF834, + 0xF8A9, 0xF91E, 0xF992, 0xFA07, 0xFA7C, 0xFAF1, 0xFB66, 0xFBDC, + 0xFC51, 0xFCC7, 0xFD3C, 0xFDB2, 0xFE28, 0xFE9E, 0xFF14, 0xFF8A +}; + +FIFO CmdQueue; +u32 SharedMem; +u32 Counter; + +Channel Channels[16]; +Sequence Sequences[16]; +Track Tracks[32]; +Alarm Alarms[8]; + +u8 ChanVolume[16]; +u8 ChanPan[16]; + +int MasterPan; +int SurroundDecay; + +// 0=regular 1=early/SM64DS +int Version; + +void ReportHardwareStatus(); +u16 UpdateCounter(); +void UnlinkChannel(Channel* chan, bool unlink); + + +void Reset() +{ + CmdQueue.Clear(); + SharedMem = 0; + Counter = 0; + + memset(Channels, 0, sizeof(Channels)); + memset(Sequences, 0, sizeof(Sequences)); + memset(Tracks, 0, sizeof(Tracks)); + memset(Alarms, 0, sizeof(Alarms)); + + memset(ChanVolume, 0, sizeof(ChanVolume)); + memset(ChanPan, 0, sizeof(ChanPan)); + + MasterPan = -1; + SurroundDecay = 0; + + for (int i = 0; i < 16; i++) + { + Channel* chan = &Channels[i]; + + chan->StatusFlags &= ~0xF9; + chan->ID = i; + } + + for (int i = 0; i < 16; i++) + { + Sequence* seq = &Sequences[i]; + + seq->StatusFlags &= ~(1<<0); + seq->ID = i; + } + + for (int i = 0; i < 32; i++) + { + Track* track = &Tracks[i]; + + track->StatusFlags &= ~(1<<0); + } + + SPU::Write16(0x04000500, 0x807F); + + Version = 0; + + if (NDSCart::CartROM) + { + u32 gamecode = *(u32*)&NDSCart::CartROM[0xC]; + if ((gamecode & 0xFFFFFF) == 0x4D5341) // ASMx / Super Mario 64 DS + Version = 1; + } + + NDS::ScheduleEvent(NDS::Event_HLE_SoundCmd, true, 174592, Process, 1); +} + + +void OnAlarm(u32 num) +{ + Alarm& alarm = Alarms[num]; + if (!alarm.Active) return; + + SendIPCReply(0x7, num | (alarm.Param << 8)); + + u32 delay = alarm.Repeat; + if (delay) + { + NDS::ScheduleEvent(NDS::Event_HLE_SoundAlarm0+num, true, delay*64, OnAlarm, num); + } + else + { + alarm.Active = false; + } +} + + +bool IsChannelPlaying(int id) +{ + u32 addr = 0x04000400 + (id*16); + + u8 cnt = SPU::Read8(addr+0x03); + return (cnt & 0x80) != 0; +} + +void StopChannel(int id, bool hold) +{ + u32 addr = 0x04000400 + (id*16); + + u32 cnt = SPU::Read32(addr); + cnt &= ~(1<<31); + if (hold) cnt |= (1<<15); + SPU::Write32(addr, cnt); +} + +int CalcChannelVolume(int vol, int pan) +{ + if (pan < 24) + { + pan += 40; + pan *= SurroundDecay; + pan += ((0x7FFF - SurroundDecay) << 6); + return (vol * pan) >> 21; + } + else if (pan >= 104) + { + pan -= 40; + pan *= (-SurroundDecay); + pan += ((0x7FFF + SurroundDecay) << 6); + return (vol * pan) >> 21; + } + + return vol; +} + +void SetupChannel_Wave(int id, u32 srcaddr, int format, int repeat, u16 looppos, u32 len, int vol, int voldiv, u16 freq, int pan) +{ + u32 addr = 0x04000400 + (id*16); + + ChanVolume[id] = vol; + ChanPan[id] = pan; + + if (MasterPan >= 0) + pan = MasterPan; + + if ((SurroundDecay > 0) && (id != 1) && (id != 3)) + vol = CalcChannelVolume(vol, pan); + + u32 cnt = (format << 29) | (repeat << 27) | (pan << 16) | (voldiv << 8) | vol; + SPU::Write32(addr+0x00, cnt); + SPU::Write16(addr+0x08, 0x10000-freq); + SPU::Write16(addr+0x0A, looppos); + SPU::Write32(addr+0x0C, len); + SPU::Write32(addr+0x04, srcaddr); +} + +void SetupChannel_PSG(int id, int duty, int vol, int voldiv, u16 freq, int pan) +{ + u32 addr = 0x04000400 + (id*16); + + ChanVolume[id] = vol; + ChanPan[id] = pan; + + if (MasterPan >= 0) + pan = MasterPan; + + if ((SurroundDecay > 0) && (id != 1) && (id != 3)) + vol = CalcChannelVolume(vol, pan); + + u32 cnt = (3 << 29) | (duty << 24) | (pan << 16) | (voldiv << 8) | vol; + SPU::Write32(addr+0x00, cnt); + SPU::Write16(addr+0x08, 0x10000-freq); +} + +void SetupChannel_Noise(int id, int vol, int voldiv, u16 freq, int pan) +{ + u32 addr = 0x04000400 + (id*16); + + ChanVolume[id] = vol; + ChanPan[id] = pan; + + if (MasterPan >= 0) + pan = MasterPan; + + if ((SurroundDecay > 0) && (id != 1) && (id != 3)) + vol = CalcChannelVolume(vol, pan); + + u32 cnt = (3 << 29) | (pan << 16) | (voldiv << 8) | vol; + SPU::Write32(addr+0x00, cnt); + SPU::Write16(addr+0x08, 0x10000-freq); +} + +void SetChannelFrequency(int id, u16 freq) +{ + u32 addr = 0x04000400 + (id*16); + + SPU::Write16(addr+0x08, 0x10000-freq); +} + +void SetChannelVolume(int id, int vol, int voldiv) +{ + u32 addr = 0x04000400 + (id*16); + + ChanVolume[id] = vol; + + if ((SurroundDecay > 0) && (id != 1) && (id != 3)) + { + int pan = SPU::Read8(addr+0x02); + vol = CalcChannelVolume(vol, pan); + } + + u16 cnt = (voldiv << 8) | vol; + SPU::Write16(addr+0x00, cnt); +} + +void SetChannelPan(int id, int pan) +{ + u32 addr = 0x04000400 + (id*16); + + ChanPan[id] = pan; + + if (MasterPan >= 0) + pan = MasterPan; + + SPU::Write8(addr+0x02, pan); + + if ((SurroundDecay > 0) && (id != 1) && (id != 3)) + { + int vol = CalcChannelVolume(ChanVolume[id], pan); + SPU::Write8(addr+0x00, vol); + } +} + +int CalcRate(int rate) +{ + if (rate == 0x7F) return 0xFFFF; + if (rate == 0x7E) return 0x3C00; + if (rate < 0x32) + { + rate = (rate << 1) + 1; + return rate & 0xFFFF; + } + + rate = 0x1E00 / (0x7E - rate); + return rate & 0xFFFF; +} + +void SetChannelAttackRate(Channel* chan, int rate) +{ + if (rate < 109) + { + chan->AttackRate = 255 - rate; + } + else + { + const u8 ratetbl[19] = + { + 0x00, 0x01, 0x05, 0x0E, 0x1A, 0x26, 0x33, 0x3F, + 0x49, 0x54, 0x5C, 0x64, 0x6D, 0x74, 0x7B, 0x7F, + 0x84, 0x89, 0x8F + }; + rate = 127 - rate; + chan->AttackRate = ratetbl[rate]; + } +} + +void SetChannelDecayRate(Channel* chan, int rate) +{ + chan->DecayRate = CalcRate(rate); +} + +void SetChannelSustainRate(Channel* chan, int rate) +{ + chan->SustainRate = rate; +} + +void SetChannelReleaseRate(Channel* chan, int rate) +{ + chan->ReleaseRate = CalcRate(rate); +} + + +bool IsCapturePlaying(int id) +{ + u32 addr = 0x04000508 + id; + + u8 cnt = SPU::Read8(addr); + return (cnt & 0x80) != 0; +} + + +void UpdateHardwareChannels() +{ + for (int i = 0; i < 16; i++) + { + Channel* chan = &Channels[i]; + if (!(chan->StatusFlags & 0xF8)) continue; + + if (chan->StatusFlags & (1<<4)) + { + StopChannel(i, false); + } + + if (chan->StatusFlags & (1<<3)) + { + if (chan->Type == 0) + { + SetupChannel_Wave(i, chan->DataAddr, chan->DataFormat, chan->Repeat?1:2, chan->LoopPos, chan->Length, chan->Volume, chan->VolumeDiv, chan->Frequency, chan->Pan); + } + else if (chan->Type == 1) + { + SetupChannel_PSG(i, chan->DataAddr, chan->Volume, chan->VolumeDiv, chan->Frequency, chan->Pan); + } + else if (chan->Type == 2) + { + SetupChannel_Noise(i, chan->Volume, chan->VolumeDiv, chan->Frequency, chan->Pan); + } + + continue; + } + + if (chan->StatusFlags & (1<<5)) + { + SetChannelFrequency(i, chan->Frequency); + } + + if (chan->StatusFlags & (1<<6)) + { + SetChannelVolume(i, chan->Volume, chan->VolumeDiv); + } + + if (chan->StatusFlags & (1<<7)) + { + SetChannelPan(i, chan->Pan); + } + } + + for (int i = 0; i < 16; i++) + { + Channel* chan = &Channels[i]; + if (!(chan->StatusFlags & 0xF8)) continue; + + if (chan->StatusFlags & (1<<3)) + { + u32 addr = 0x04000400 + (i*16) + 0x03; + SPU::Write8(addr, SPU::Read8(addr) | 0x80); + } + + chan->StatusFlags &= ~0xF8; + } +} + + +void InitTrack(Track* track) +{ + track->NoteBuffer = 0; + track->CurNoteAddr = 0; + + track->StatusFlags |= 0x42; + track->StatusFlags &= ~0xBC; + + track->LoopLevel = 0; + track->InstrIndex = 0; + track->Priority = 64; + track->Volume = 127; + track->Expression = 127; + track->TrackUnk0A = 0; + track->Pan = 0; + track->TrackUnk09 = 0; + track->PitchBend = 0; + track->Frequency = 0; + track->AttackRate = 255; + track->DecayRate = 255; + track->SustainRate = 255; + track->ReleaseRate = 255; + track->TrackUnk01 = 127; + track->PitchBendRange = 2; + track->TrackUnk14 = 60; + track->PortamentoTime = 0; + track->SweepPitch = 0; + track->Transpose = 0; + track->ChannelMask = 0xFFFF; + + track->ModulationType = 0; + track->ModulationDepth = 0; + track->ModulationRange = 1; + track->ModulationSpeed = 16; + track->ModulationDelay = 0; + + track->RestCounter = 0; + track->ChanList = nullptr; +} + +void ReleaseTrack(Track* track, Sequence* seq, bool flag) +{ + int volbase3 = BaseVolumeTable[seq->Volume] + BaseVolumeTable[track->Volume] + BaseVolumeTable[track->Expression]; + if (volbase3 < -0x8000) volbase3 = -0x8000; + + int volbase1 = track->TrackUnk0A + seq->SeqUnk06; + if (volbase1 < -0x8000) volbase1 = -0x8000; + + int freqbase = track->Frequency + ((track->PitchBend * (track->PitchBendRange << 6)) >> 7); + + int panbase = track->Pan; + if (track->TrackUnk01 != 0x7F) + panbase = ((panbase * track->TrackUnk01) + 64) >> 7; + panbase += track->TrackUnk09; + if (panbase < -0x80) panbase = -0x80; + else if (panbase > 0x7F) panbase = 0x7F; + + Channel* chan = track->ChanList; + while (chan) + { + chan->VolBase1 = volbase1; + if (chan->VolRampPhase != 3) + { + chan->VolBase3 = volbase3; + chan->FreqBase3 = freqbase; + chan->PanBase3 = panbase; + chan->PanBase1 = track->TrackUnk01; + chan->ModulationType = track->ModulationType; + chan->ModulationSpeed = track->ModulationSpeed; + chan->ModulationDepth = track->ModulationDepth; + chan->ModulationRange = track->ModulationRange; + chan->ModulationDelay = track->ModulationDelay; + + if ((chan->NoteLength == 0) && flag) + { + chan->Priority = 1; + chan->VolRampPhase = 3; + } + } + + chan = chan->Next; + } +} + +void FinishTrack(Track* track, Sequence* seq, int rate) +{ + ReleaseTrack(track, seq, false); + + Channel* chan = track->ChanList; + while (chan) + { + if (chan->StatusFlags & (1<<0)) + { + if (rate >= 0) + SetChannelReleaseRate(chan, rate & 0xFF); + + chan->Priority = 1; + chan->VolRampPhase = 3; + } + + chan = chan->Next; + } +} + +void UnlinkTrackChannels(Track* track) +{ + Channel* chan = track->ChanList; + while (chan) + { + chan->Linked = false; + chan->LinkedTrack = nullptr; + + chan = chan->Next; + } + + track->ChanList = nullptr; +} + +int FindFreeTrack() +{ + for (int i = 0; i < 32; i++) + { + Track* track = &Tracks[i]; + + if (!(track->StatusFlags & (1<<0))) + { + track->StatusFlags |= (1<<0); + return i; + } + } + + return -1; +} + +Track* GetSequenceTrack(Sequence* seq, int id) +{ + if (id > 15) return nullptr; + + id = seq->Tracks[id]; + if (id == 0xFF) return nullptr; + return &Tracks[id]; +} + +void FinishSequenceTrack(Sequence* seq, int id) +{ + Track* track = GetSequenceTrack(seq, id); + if (!track) return; + + FinishTrack(track, seq, -1); + UnlinkTrackChannels(track); + + track->StatusFlags &= ~(1<<0); + seq->Tracks[id] = 0xFF; +} + +void InitSequence(Sequence* seq, u32 sbnk) +{ + seq->StatusFlags &= ~(1<<2); + + seq->SBNKAddr = sbnk; + seq->Tempo = 0x78; + seq->SeqUnk1A = 0x100; + seq->TickCounter = 240; + seq->Volume = 127; + seq->SeqUnk06 = 0; + seq->Pan = 64; + + for (int i = 0; i < 16; i++) + seq->Tracks[i] = 0xFF; + + if (SharedMem) + { + u32 seqdata = SharedMem + (seq->ID * 0x24); + + NDS::ARM7Write32(seqdata+0x40, 0); + for (int i = 0; i < 16; i++) + { + NDS::ARM7Write16(seqdata+0x20 + (i<<1), 0xFFFF); + } + } +} + +void FinishSequence(Sequence* seq) +{ + for (int i = 0; i < 16; i++) + FinishSequenceTrack(seq, i); + + seq->StatusFlags &= ~(1<<0); +} + + +void PrepareSequence(int id, u32 notedata, u32 noteoffset, u32 sbnk) +{ + Sequence* seq = &Sequences[id]; + + if (seq->StatusFlags & (1<<0)) + FinishSequence(seq); + + InitSequence(seq, sbnk); + int tnum = FindFreeTrack(); + if (tnum < 0) return; + + Track* track0 = &Tracks[tnum]; + InitTrack(track0); + track0->NoteBuffer = notedata; + track0->CurNoteAddr = track0->NoteBuffer + noteoffset; + seq->Tracks[0] = tnum; + + u8 firstcmd = NDS::ARM7Read8(track0->CurNoteAddr); + if (firstcmd == 0xFE) + { + // multi track setup + + track0->CurNoteAddr++; + + u16 mask = NDS::ARM7Read8(track0->CurNoteAddr++); + mask |= (NDS::ARM7Read8(track0->CurNoteAddr++) << 8); + + for (int i = 1; i < 16; i++) + { + if (!(mask & (1<Tracks[i] = tnum2; + } + } + + seq->StatusFlags |= (1<<0); + seq->StatusFlags &= ~(1<<1); + + if (SharedMem) + { + u32 mask = NDS::ARM7Read32(SharedMem+4); + mask |= (1<StatusFlags |= (1<<1); +} + +void ProcessCommands() +{ + const u32 cmd_trans_early[30] = + { + 0x0, 0x1, 0x4, 0x6, 0x7, 0x8, 0x9, 0xA, + 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x21, 0x1E, 0x1F, 0x20 + }; + + while (!CmdQueue.IsEmpty()) + { + u32 cmdbuf = CmdQueue.Read(); + for (;;) + { + if (!cmdbuf) + { + u32 val = NDS::ARM7Read32(SharedMem); + val++; + NDS::ARM7Write32(SharedMem, val); + + break; + } + + u32 next = NDS::ARM7Read32(cmdbuf+0x00); + u32 cmd = NDS::ARM7Read32(cmdbuf+0x04); + + u32 args[4]; + for (u32 i = 0; i < 4; i++) + args[i] = NDS::ARM7Read32(cmdbuf+0x8 + (i*4)); + + if (Version == 1) + { + // COMMAND TRANSLATE for SM64DS (early sound engine version) + cmd = cmd_trans_early[cmd]; + } + + switch (cmd) + { + case 0x0: // play sequence, directly + { + PrepareSequence(args[0], args[1], args[2], args[3]); + StartSequence(args[0]); + } + break; + + case 0x1: // stop sequence + { + u32 id = args[0]; + Sequence* seq = &Sequences[id]; + if (!(seq->StatusFlags & (1<<0))) break; + + FinishSequence(seq); + + if (SharedMem) + { + u32 mask = NDS::ARM7Read32(SharedMem+4); + mask &= ~(1<Pan = val & 0xFF; break; + case 0x105: seq->Volume = val & 0xFF; break; + + case 0x206: seq->SeqUnk06 = val & 0xFFFF; break; + case 0x218: seq->Tempo = val & 0xFFFF; break; + case 0x21A: seq->SeqUnk1A = val & 0xFFFF; break; + case 0x21C: seq->TickCounter = val & 0xFFFF; break; + + default: + printf("cmd6: unknown write %08X %08X\n", key, val); + break; + } + } + break; + + case 0x7: // set track param + { + // normally, writes to the track structure + + Sequence* seq = &Sequences[args[0] & 0xFFFFFF]; + u32 trackmask = args[1]; + u32 key = ((args[0] >> 24) << 8) | args[2]; + u32 val = args[3]; + + for (int i = 0; i < 16; i++) + { + if (!(trackmask & (1<Volume = val & 0xFF; break; + case 0x105: track->Expression = val & 0xFF; break; + case 0x106: track->PitchBend = val & 0xFF; break; + case 0x107: track->PitchBendRange = val & 0xFF; break; + case 0x108: track->Pan = val & 0xFF; break; + case 0x109: track->TrackUnk09 = val & 0xFF; break; + + case 0x20A: track->TrackUnk0A = val & 0xFFFF; break; + case 0x20C: track->Frequency = val & 0xFFFF; break; + + default: + printf("cmd7: unknown write %08X %08X\n", key, val); + break; + } + } + } + break; + + case 0x9: // set track channel mask + { + Sequence* seq = &Sequences[args[0]]; + u32 trackmask = args[1]; + u32 chanmask = args[2] & 0xFFFF; + + for (int i = 0; i < 16; i++) + { + if (!(trackmask & (1<ChannelMask = chanmask; + track->StatusFlags |= (1<<7); + } + } + break; + + case 0xA: // write to sharedmem + { + u32 addr = SharedMem + (args[0] * 0x24) + (args[1] * 2) + 0x20; + NDS::ARM7Write16(addr, args[2] & 0xFFFF); + } + break; + + case 0xC: // start + { + u32 mask_chan = args[0]; + u32 mask_cap = args[1]; + u32 mask_alarm = args[2]; + + for (int i = 0; i < 16; i++) + { + if (!(mask_chan & (1<> 24) & 0x3; + int repeat = (args[3] >> 26) & 0x3; + u16 looppos = args[3] & 0xFFFF; + u32 len = args[2] & 0x3FFFFF; + int vol = (args[2] >> 24) & 0x7F; + int voldiv = (args[2] >> 22) & 0x3; + u16 freq = args[0] >> 16; + int pan = (args[3] >> 16) & 0x7F; + + SetupChannel_Wave(id, srcaddr, format, repeat, looppos, len, vol, voldiv, freq, pan); + } + break; + + case 0xF: // setup channel (PSG) + { + int id = args[0]; + int duty = args[3]; + int vol = args[1] & 0x7F; + int voldiv = (args[1] >> 8) & 0x3; + u16 freq = (args[2] >> 8) & 0xFFFF; + int pan = args[2] & 0x7F; + + SetupChannel_PSG(id, duty, vol, voldiv, freq, pan); + } + break; + + case 0x10: // setup channel (noise) + { + int id = args[0]; + int vol = args[1] & 0x7F; + int voldiv = (args[1] >> 8) & 0x3; + u16 freq = (args[2] >> 8) & 0xFFFF; + int pan = args[2] & 0x7F; + + SetupChannel_Noise(id, vol, voldiv, freq, pan); + } + break; + + case 0x11: // setup capture + { + u32 dstaddr = args[0]; + u16 len = args[1] & 0xFFFF; + + u32 num = (args[2] >> 31) & 0x1; + + u32 cnt = ((args[2] >> 30) & 0x1) << 3; + cnt |= (((args[2] >> 29) & 0x1) ? 0 : 0x04); + cnt |= ((args[2] >> 28) & 0x1) << 1; + cnt |= ((args[2] >> 27) & 0x1); + + SPU::Write8(0x04000508+num, cnt); + SPU::Write32(0x04000510+(num*8), dstaddr); + SPU::Write16(0x04000514+(num*8), len); + } + break; + + case 0x12: // setup alarm + { + u32 num = args[0]; + + Alarm& alarm = Alarms[num & 0x7]; + alarm.Delay = args[1]; + alarm.Repeat = args[2]; + alarm.Param = args[3] & 0xFF; + alarm.Active = false; // checkme + } + break; + + case 0x14: // set channel volume + { + for (int i = 0; i < 16; i++) + { + if (!(args[0] & (1<= 0) + { + u8 pan = MasterPan & 0xFF; + for (int i = 0; i < 16; i++) + { + SPU::Write8(0x04000402 + (i*16), pan); + } + } + else + { + for (int i = 0; i < 16; i++) + { + SPU::Write8(0x04000402 + (i*16), ChanPan[i]); + } + } + } + break; + + case 0x19: // output sel + { + u32 outputL = args[0]; + u32 outputR = args[1]; + u32 mixch1 = args[2]; + u32 mixch3 = args[3]; + + u8 cnt = NDS::ARM7Read8(0x04000501); + cnt &= 0x80; + cnt |= (outputL & 0x3); + cnt |= (outputR & 0x3) << 2; + cnt |= (mixch1 & 0x1) << 4; + cnt |= (mixch3 & 0x1) << 5; + NDS::ARM7Write8(0x04000501, cnt); + } + break; + + case 0x1A: + { + // + printf("unknown sound cmd %08X, %08X %08X %08X %08X\n", + cmd, args[0], args[1], args[2], args[3]); + } + break; + + case 0x1D: + SharedMem = args[0]; + break; + + case 0x20: + { + // cmdbuf+08 and cmdbuf+0C = bounds + // stop channels conditionally + printf("unknown sound cmd %08X, %08X %08X %08X %08X\n", + cmd, args[0], args[1], args[2], args[3]); + } + break; + + default: + printf("unknown sound cmd %08X, %08X %08X %08X %08X\n", + cmd, args[0], args[1], args[2], args[3]); + break; + } + + cmdbuf = next; + } + } +} + + +bool ReadInstrument(u32 sbnk, int index, int tune, u8* out) +{ + if (index < 0) return false; + + u32 numinstr = NDS::ARM7Read32(sbnk + 0x38); + if ((u32)index >= numinstr) return false; + + u32 val = NDS::ARM7Read32(sbnk + 0x3C + (index << 2)); + out[0] = val & 0xFF; + if (out[0] >= 1 && out[0] <= 5) + { + u32 addr = sbnk + (val >> 8); + for (int i = 0; i < 5; i++) + { + *(u16*)&out[2 + (i << 1)] = NDS::ARM7Read16(addr + (i << 1)); + } + return true; + } + else if (out[0] == 16) + { + u32 addr = sbnk + (val >> 8); + u8 lower = NDS::ARM7Read8(addr); + u8 upper = NDS::ARM7Read8(addr + 1); + + if (tune < lower) return false; + if (tune > upper) return false; + + addr += ((tune - lower) * 0xC) + 2; + for (int i = 0; i < 6; i++) + { + *(u16*)&out[(i << 1)] = NDS::ARM7Read16(addr + (i << 1)); + } + return true; + } + else if (out[0] == 17) + { + u32 addr = sbnk + (val >> 8); + + int num = -1; + for (int i = 0; i < 8; i++) + { + u8 val = NDS::ARM7Read8(addr + i); + if (tune > val) continue; + + num = i; + break; + } + + if (num < 0) return false; + + addr += (num * 0xC) + 8; + for (int i = 0; i < 6; i++) + { + *(u16*)&out[(i << 1)] = NDS::ARM7Read16(addr + (i << 1)); + } + return true; + } + + return false; +} + +void InitInstrumentChannel(Channel* chan, int len) +{ + chan->BaseVolume = -92544; + chan->VolRampPhase = 0; + chan->NoteLength = len; + chan->ModulationCount1 = 0; + chan->ModulationCount2 = 0; + chan->StatusFlags |= 0x03; +} + +bool SetupInstrument(Channel* chan, int tune, int speed, int len, u32 sbnk, u8* data) +{ + u8 release = data[0x0A]; + if (release == 0xFF) + { + release = 0; + len = -1; + } + + switch (data[0x00]) + { + case 1: // wave + case 4: + { + u32 swav; + if (data[0x00] != 1) + { + // direct pointer + + swav = (*(u16*)&data[0x02]) | ((*(u16*)&data[0x04]) << 16); + } + else + { + // load from SBNK + + u16 swav_num = *(u16*)&data[0x02]; + u16 swar_num = *(u16*)&data[0x04]; + + u32 swar = NDS::ARM7Read32(sbnk + 0x18 + (swar_num << 3)); + if (!swar) return false; + + u32 num_samples = NDS::ARM7Read32(swar + 0x38); + if (swav_num >= num_samples) return false; + + swav = NDS::ARM7Read32(swar + 0x3C + (swav_num << 2)); + if (!swav) return false; + if (swav < 0x02000000) + swav += swar; + } + + if (!swav) return false; + + chan->Type = 0; // wave + chan->DataFormat = NDS::ARM7Read8(swav+0x00); + chan->Repeat = NDS::ARM7Read8(swav+0x01); + chan->SampleRate = NDS::ARM7Read16(swav+0x02); + chan->SwavFrequency = NDS::ARM7Read16(swav+0x04); + chan->LoopPos = NDS::ARM7Read16(swav+0x06); + chan->Length = NDS::ARM7Read32(swav+0x08); + chan->DataAddr = swav+0xC; + } + break; + + case 2: // PSG + { + u16 duty = *(u16*)&data[0x02]; + + if ((chan->ID < 8) || (chan->ID > 13)) + return false; + + chan->Type = 1; // PSG + chan->DataAddr = duty; + chan->SwavFrequency = 0x1F46; + } + break; + + case 3: // noise + { + if ((chan->ID < 14) || (chan->ID > 15)) + return false; + + chan->Type = 2; // noise + chan->SwavFrequency = 0x1F46; + } + break; + + default: + return false; + } + + InitInstrumentChannel(chan, len); + chan->FreqBase2 = tune; + chan->FreqBase1 = *(u16*)&data[0x06]; // note number + chan->VolBase2 = speed; + SetChannelAttackRate(chan, data[0x07]); + SetChannelDecayRate(chan, data[0x08]); + SetChannelSustainRate(chan, data[0x09]); + SetChannelReleaseRate(chan, release); + chan->PanBase2 = data[0x0B] - 64; + return true; +} + +Channel* AllocateChannel(u16 chanmask, int prio, bool flag, Track* track) +{ + const int chanorder[16] = {4, 5, 6, 7, 2, 0, 3, 1, 8, 9, 10, 11, 14, 12, 15, 13}; + const int voldiv[4] = {0, 1, 2, 4}; + Channel* ret = nullptr; + + // TODO: channel lock masks + chanmask &= 0xFFF5; + + for (int i = 0; i < 16; i++) + { + int id = chanorder[i]; + if (!(chanmask & (1 << id))) continue; + + Channel* chan = &Channels[id]; + if (!ret) + { + ret = chan; + continue; + } + + if (chan->Priority > ret->Priority) + continue; + + if (chan->Priority == ret->Priority) + { + u32 vol1 = (chan->Volume << 4) >> voldiv[chan->VolumeDiv]; + u32 vol2 = (ret->Volume << 4) >> voldiv[ret->VolumeDiv]; + + if (vol1 >= vol2) + continue; + } + + ret = chan; + } + + if (!ret) return nullptr; + if (prio < ret->Priority) return nullptr; + + if (ret->Linked) + UnlinkChannel(ret, false); + + ret->StatusFlags &= ~0xF9; + ret->StatusFlags |= (1<<4); + + ret->Next = nullptr; + ret->Linked = true; + ret->LinkedTrack = track; + ret->NoteLength = 0; + ret->Priority = prio; + ret->Volume = 127; + ret->VolumeDiv = 0; + ret->StatusFlags &= ~(1<<1); + ret->StatusFlags |= (1<<2); + ret->FreqBase2 = 60; + ret->FreqBase1 = 60; + ret->VolBase2 = 127; + ret->PanBase2 = 0; + ret->VolBase3 = 0; + ret->VolBase1 = 0; + ret->FreqBase3 = 0; + ret->PanBase3 = 0; + ret->PanBase1 = 127; + ret->FreqRampTarget = 0; + ret->FreqRampLen = 0; + ret->FreqRampPos = 0; + SetChannelAttackRate(ret, 127); + SetChannelDecayRate(ret, 127); + SetChannelSustainRate(ret, 127); + SetChannelReleaseRate(ret, 127); + ret->ModulationType = 0; + ret->ModulationDepth = 0; + ret->ModulationRange = 1; + ret->ModulationSpeed = 16; + ret->ModulationDelay = 0; + + return ret; +} + +void TrackKeyOn(Track* track, Sequence* seq, int tune, int speed, int len) +{ + //printf("Sound_Nitro: KEY ON, seq=%d, tune=%d speed=%d len=%d\n", seq->ID, tune, speed, len); + + Channel* chan = nullptr; + if (track->StatusFlags & (1<<3)) + { + chan = track->ChanList; + if (chan) + { + chan->FreqBase2 = tune; + chan->VolBase2 = speed; + } + } + + if (!chan) + { + // allocate channel + + u8 instrdata[16]; + if (!ReadInstrument(seq->SBNKAddr, track->InstrIndex, tune, instrdata)) + return; + + u16 chanmask; + switch (instrdata[0]) + { + case 1: + case 4: chanmask = 0xFFFF; break; + case 2: chanmask = 0x3F00; break; + case 3: chanmask = 0xC000; break; + default: return; + } + + chanmask &= track->ChannelMask; + chan = AllocateChannel(chanmask, track->Priority, (track->StatusFlags & (1<<7)) != 0, track); + if (!chan) return; + + int _len; + if (track->StatusFlags & (1<<3)) + _len = -1; + else + _len = len; + + if (!SetupInstrument(chan, tune, speed, _len, seq->SBNKAddr, instrdata)) + { + chan->Priority = 0; + chan->Linked = false; + chan->LinkedTrack = nullptr; + return; + } + + chan->Next = track->ChanList; + track->ChanList = chan; + } + + if (track->AttackRate != 0xFF) + SetChannelAttackRate(chan, track->AttackRate); + if (track->DecayRate != 0xFF) + SetChannelDecayRate(chan, track->DecayRate); + if (track->SustainRate != 0xFF) + SetChannelSustainRate(chan, track->SustainRate); + if (track->ReleaseRate != 0xFF) + SetChannelReleaseRate(chan, track->ReleaseRate); + + chan->FreqRampTarget = track->SweepPitch; + if (track->StatusFlags & (1<<5)) + { + chan->FreqRampTarget += (((track->TrackUnk14 - tune) << 22) >> 16); + } + + if (!track->PortamentoTime) + { + chan->FreqRampLen = len; + chan->StatusFlags &= ~(1<<2); + } + else + { + int time = track->PortamentoTime; + time *= time; + + int target = chan->FreqRampTarget; + if (target < 0) target = -target; + time *= target; + chan->FreqRampLen = time >> 11; + } + chan->FreqRampPos = 0; +} + +u32 GetNoteParamAddr(Sequence* seq, u8 idx) +{ + if (!SharedMem) return 0; + + if (idx >= 0x10) + { + return SharedMem + 0x260 + ((idx-0x10) << 1); + } + + return SharedMem + 0x20 + (seq->ID * 0x24) + (idx << 1); +} + +u32 ReadNoteOpParam(Track* track, Sequence* seq, int type) +{ + switch (type) + { + case 0: // 8-bit value + { + u8 val = NDS::ARM7Read8(track->CurNoteAddr++); + return val; + } + + case 1: // 16-bit value + { + u32 val = NDS::ARM7Read8(track->CurNoteAddr++); + val |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + return val; + } + + case 2: // variable length + { + u32 val = 0; + for (;;) + { + u8 byte = NDS::ARM7Read8(track->CurNoteAddr++); + val = (val << 7) | (byte & 0x7F); + if (!(byte & 0x80)) break; + } + return val; + } + + case 3: // based on counter + { + s16 val1 = NDS::ARM7Read8(track->CurNoteAddr++); + val1 |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + s16 val2 = NDS::ARM7Read8(track->CurNoteAddr++); + val2 |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + + u16 cnt = UpdateCounter(); + int res = ((val2 - val1) + 1) * cnt; + res = val1 + (res >> 16); + return res; + } + + case 4: // variable in sharedmem + { + u8 idx = NDS::ARM7Read8(track->CurNoteAddr++); + u32 addr = GetNoteParamAddr(seq, idx); + if (addr) + { + u32 val = NDS::ARM7Read16(addr); + return ((s32)(val << 16)) >> 16; + } + + return 0; + } + } + + return 0; +} + +int UpdateTrack(Track* track, Sequence* seq, int id, bool process) +{ + Channel* chan = track->ChanList; + while (chan) + { + if (chan->NoteLength > 0) + chan->NoteLength--; + + if (!(chan->StatusFlags & (1<<2))) + { + if (chan->FreqRampPos < chan->FreqRampLen) + chan->FreqRampPos++; + } + + chan = chan->Next; + } + + if (track->StatusFlags & (1<<4)) + { + if (track->ChanList) return 0; + track->StatusFlags &= ~(1<<4); + } + + if (track->RestCounter > 0) + { + track->RestCounter--; + if (track->RestCounter > 0) + return 0; + } + + while (!track->RestCounter) + { + if (track->StatusFlags & (1<<4)) break; + + bool cond = true; + int paramtype = 2; + + u8 note_op = NDS::ARM7Read8(track->CurNoteAddr++); + if (note_op == 0xA2) + { + note_op = NDS::ARM7Read8(track->CurNoteAddr++); + cond = (track->StatusFlags & (1<<6)) != 0; + } + if (note_op == 0xA0) + { + note_op = NDS::ARM7Read8(track->CurNoteAddr++); + paramtype = 3; + } + if (note_op == 0xA1) + { + note_op = NDS::ARM7Read8(track->CurNoteAddr++); + paramtype = 4; + } + + if (!(note_op & 0x80)) + { + // key on + + u8 speed = NDS::ARM7Read8(track->CurNoteAddr++); + s32 len = ReadNoteOpParam(track, seq, paramtype); + int tune = note_op + track->Transpose; + if (!cond) continue; + + if (tune < 0) tune = 0; + else if (tune > 127) tune = 127; + + if ((!(track->StatusFlags & (1<<2))) && process) + { + TrackKeyOn(track, seq, tune, speed, (len<=0) ? -1 : len); + } + + track->TrackUnk14 = tune; + if (track->StatusFlags & (1<<1)) + { + track->RestCounter = len; + if (!len) track->StatusFlags |= (1<<4); + } + } + else + { + switch (note_op & 0xF0) + { + case 0x80: + { + u32 param = ReadNoteOpParam(track, seq, paramtype); + if (!cond) break; + + switch (note_op) + { + case 0x80: // rest + track->RestCounter = param; + break; + + case 0x81: // bank/program number + if ((s32)param < 0x10000) + track->InstrIndex = param; + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + case 0x90: + { + switch (note_op) + { + case 0x93: // setup track + { + u8 tnum = NDS::ARM7Read8(track->CurNoteAddr++); + u32 trackaddr = NDS::ARM7Read8(track->CurNoteAddr++); + trackaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + trackaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 16); + if (!cond) break; + + Track* thetrack = GetSequenceTrack(seq, tnum); + if (!thetrack) break; + if (thetrack == track) break; + + FinishTrack(thetrack, seq, -1); + UnlinkTrackChannels(thetrack); + + thetrack->NoteBuffer = track->NoteBuffer; + thetrack->CurNoteAddr = thetrack->NoteBuffer + trackaddr; + } + break; + + case 0x94: // jump + { + u32 jumpaddr = NDS::ARM7Read8(track->CurNoteAddr++); + jumpaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + jumpaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 16); + if (!cond) break; + + track->CurNoteAddr = track->NoteBuffer + jumpaddr; + } + break; + + case 0x95: // call + { + u32 jumpaddr = NDS::ARM7Read8(track->CurNoteAddr++); + jumpaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 8); + jumpaddr |= (NDS::ARM7Read8(track->CurNoteAddr++) << 16); + if (!cond) break; + + if (track->LoopLevel >= 3) break; + track->LoopAddr[track->LoopLevel] = track->CurNoteAddr; + track->LoopLevel++; + track->CurNoteAddr = track->NoteBuffer + jumpaddr; + } + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + case 0xB0: + { + u8 idx = NDS::ARM7Read8(track->CurNoteAddr++); + if (paramtype == 2) paramtype = 1; + s16 param = (s16)((s32)(ReadNoteOpParam(track, seq, paramtype) << 16) >> 16); + u32 paramaddr = GetNoteParamAddr(seq, idx); + if (!cond) break; + if (!paramaddr) break; + + switch (note_op) + { + case 0xB0: // set + NDS::ARM7Write16(paramaddr, param); + break; + case 0xB1: // add + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) + param); + break; + case 0xB2: // sub + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) - param); + break; + case 0xB3: // mul + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) * param); + break; + case 0xB4: // div + if (!param) break; + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) / param); + break; + case 0xB5: // shift + if (param >= 0) + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) << param); + else + NDS::ARM7Write16(paramaddr, (s16)NDS::ARM7Read16(paramaddr) >> (-param)); + break; + case 0xB6: // + { + bool neg = false; + if (param < 0) + { + neg = true; + param = -param; + } + + u16 cnt = UpdateCounter(); + int val = ((int)cnt * (param + 1)) >> 16; + if (neg) val = -val; + NDS::ARM7Write16(paramaddr, val); + } + break; + case 0xB8: // compare == + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) == param) + track->StatusFlags |= (1<<6); + break; + case 0xB9: // compare >= + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) >= param) + track->StatusFlags |= (1<<6); + break; + case 0xBA: // compare > + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) > param) + track->StatusFlags |= (1<<6); + break; + case 0xBB: // compare <= + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) <= param) + track->StatusFlags |= (1<<6); + break; + case 0xBC: // compare < + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) < param) + track->StatusFlags |= (1<<6); + break; + case 0xBD: // compare != + track->StatusFlags &= ~(1<<6); + if ((s16)NDS::ARM7Read16(paramaddr) != param) + track->StatusFlags |= (1<<6); + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + case 0xC0: + case 0xD0: + { + if (paramtype == 2) paramtype = 0; + u8 param = (u8)ReadNoteOpParam(track, seq, paramtype); + if (!cond) break; + + switch (note_op) + { + case 0xC0: // pan + track->Pan = param - 64; + break; + case 0xC1: // volume + track->Volume = param; + break; + case 0xC2: // master volume + seq->Volume = param; + break; + case 0xC3: // transpose + track->Transpose = param; + break; + case 0xC4: // pitch bend + track->PitchBend = param; + break; + case 0xC5: // pitch bend range + track->PitchBendRange = param; + break; + case 0xC6: // priority + track->Priority = param; + break; + case 0xC7: // mono/poly + track->StatusFlags &= ~(1<<1); + track->StatusFlags |= ((param & 0x1) << 1); + break; + case 0xC8: // tie + track->StatusFlags &= ~(1<<3); + track->StatusFlags |= ((param & 0x1) << 3); + FinishTrack(track, seq, -1); + UnlinkTrackChannels(track); + break; + case 0xC9: // portamento control + track->TrackUnk14 = param + track->Transpose; + track->StatusFlags |= (1<<5); + if (Version == 1) + { + FinishTrack(track, seq, -1); + UnlinkTrackChannels(track); + } + break; + case 0xCA: // modulation depth + track->ModulationDepth = param; + break; + case 0xCB: // modulation speed + track->ModulationSpeed = param; + break; + case 0xCC: // modulation type + track->ModulationType = param; + break; + case 0xCD: // modulation range + track->ModulationRange = param; + break; + case 0xCE: // portamento on/off + track->StatusFlags &= ~(1<<5); + track->StatusFlags |= ((param & 0x1) << 5); + if (Version == 1) + { + FinishTrack(track, seq, -1); + UnlinkTrackChannels(track); + } + break; + case 0xCF: // portamento time + track->PortamentoTime = param; + break; + case 0xD0: // attack rate + track->AttackRate = param; + break; + case 0xD1: // decay rate + track->DecayRate = param; + break; + case 0xD2: // sustain rate + track->SustainRate = param; + break; + case 0xD3: // release rate + track->ReleaseRate = param; + break; + case 0xD4: // loop start marker + if (track->LoopLevel >= 3) break; + track->LoopAddr[track->LoopLevel] = track->CurNoteAddr; + track->LoopCount[track->LoopLevel] = param; + track->LoopLevel++; + break; + case 0xD5: // expression + track->Expression = param; + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + case 0xE0: + { + if (paramtype == 2) paramtype = 1; + s16 param = (s16)((s32)(ReadNoteOpParam(track, seq, paramtype) << 16) >> 16); + if (!cond) break; + + switch (note_op) + { + case 0xE0: // modulation delay + track->ModulationDelay = param; + break; + case 0xE1: // tempo + seq->Tempo = param; + break; + case 0xE3: // sweep pitch + track->SweepPitch = param; + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + case 0xF0: + { + if (!cond) break; + switch (note_op) + { + case 0xFC: // loop end marker + { + if (track->LoopLevel == 0) break; + + int level = track->LoopLevel - 1; + u8 cnt = track->LoopCount[level]; + if (cnt != 0) + { + cnt--; + if (!cnt) + { + track->LoopLevel--; + break; + } + } + + track->LoopCount[level] = cnt; + track->CurNoteAddr = track->LoopAddr[level]; + } + break; + + case 0xFD: // return from call + { + if (track->LoopLevel == 0) break; + + int level = track->LoopLevel - 1; + track->CurNoteAddr = track->LoopAddr[level]; + track->LoopLevel--; + } + break; + + case 0xFE: // multitrack marker (handled at sequence setup) + break; + case 0xFF: // track end + return -1; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + break; + + default: + printf("Sound_Nitro: unknown note op %02X\n", note_op); + break; + } + } + } + + return 0; +} + +bool UpdateSequenceTracks(Sequence* seq, bool process) +{ + bool ret = true; + + for (int i = 0; i < 16; i++) + { + Track* track = GetSequenceTrack(seq, i); + if (!track) continue; + if (!track->CurNoteAddr) continue; + + if (!UpdateTrack(track, seq, i, process)) + { + ret = false; + continue; + } + + FinishSequenceTrack(seq, i); + } + + return ret; +} + +void UpdateSequence(Sequence* seq) +{ + int cnt = 0; + while (seq->TickCounter >= 240) + { + seq->TickCounter -= 240; + cnt++; + } + + int i; + for (i = 0; i < cnt; i++) + { + if (UpdateSequenceTracks(seq, true)) + { + FinishSequence(seq); + break; + } + } + + if (SharedMem) + { + u32 addr = SharedMem + 0x40 + (seq->ID * 0x24); + NDS::ARM7Write32(addr, NDS::ARM7Read32(addr) + i); + } + + seq->TickCounter += ((seq->Tempo * seq->SeqUnk1A) >> 8); +} + +void ProcessSequences(bool update) +{ + u16 activemask = 0; + + for (int i = 0; i < 16; i++) + { + Sequence* seq = &Sequences[i]; + if (!(seq->StatusFlags & (1<<0))) continue; + + if (seq->StatusFlags & (1<<1)) + { + if (update && (!(seq->StatusFlags & (1<<2)))) + UpdateSequence(seq); + + for (int j = 0; j < 16; j++) + { + Track* track = GetSequenceTrack(seq, j); + if (!track) continue; + + ReleaseTrack(track, seq, true); + } + } + + if (seq->StatusFlags & (1<<0)) + activemask |= (1<LinkedTrack; + + if (unlink) + { + chan->Priority = 0; + chan->Linked = false; + chan->LinkedTrack = nullptr; + } + + if (track->ChanList == chan) + { + track->ChanList = chan->Next; + return; + } + + Channel* chan2 = track->ChanList; + do + { + if (chan2->Next == chan) + { + chan2->Next = chan->Next; + return; + } + + chan2 = chan2->Next; + } + while (chan2); +} + +int ChannelVolumeRamp(Channel* chan, bool update) +{ + if (update) + { + if (chan->VolRampPhase == 0) + { + // attack + + chan->BaseVolume = -(((-chan->BaseVolume) * chan->AttackRate) >> 8); + if (chan->BaseVolume == 0) + chan->VolRampPhase = 1; + } + else if (chan->VolRampPhase == 1) + { + // decay + + chan->BaseVolume -= chan->DecayRate; + int target = BaseVolumeTable[chan->SustainRate & 0x7F] << 7; + if (chan->BaseVolume <= target) + { + chan->BaseVolume = target; + chan->VolRampPhase = 2; + } + } + else if (chan->VolRampPhase == 3) + { + // release + + chan->BaseVolume -= chan->ReleaseRate; + } + } + + return chan->BaseVolume >> 7; +} + +int ChannelFreqRamp(Channel* chan, bool update) +{ + if (chan->FreqRampTarget == 0) + return 0; + + if (chan->FreqRampPos >= chan->FreqRampLen) + return 0; + + s64 tmp = (s64)chan->FreqRampTarget * (s64)(chan->FreqRampLen-chan->FreqRampPos); + int ret = (int)(tmp / (s64)chan->FreqRampLen); + + if (update) + { + if (chan->StatusFlags & (1<<2)) + chan->FreqRampPos++; + } + + return ret; +} + +int ChannelModulation(Channel* chan, bool update) +{ + s64 modfactor = 0; + if ((chan->ModulationDepth != 0) && + (chan->ModulationCount1 >= chan->ModulationDelay)) + { + int index = chan->ModulationCount2 >> 8; + if (index < 32) + modfactor = ModulationTable[index]; + else if (index < 64) + modfactor = ModulationTable[64 - index]; + else if (index < 96) + modfactor = -ModulationTable[index - 64]; + else + modfactor = -ModulationTable[32 - (index - 96)]; + + modfactor *= chan->ModulationDepth; + modfactor *= chan->ModulationRange; + } + + if (modfactor) + { + switch (chan->ModulationType) + { + case 0: + modfactor <<= 6; + break; + case 1: + modfactor *= 60; + break; + case 2: + modfactor <<= 6; + break; + } + + modfactor >>= 14; + } + + if (update) + { + if (chan->ModulationCount1 < chan->ModulationDelay) + { + chan->ModulationCount1++; + } + else + { + u32 cnt = (chan->ModulationCount2 + (chan->ModulationSpeed << 6)) >> 8; + while (cnt >= 128) cnt -= 128; + + chan->ModulationCount2 += (chan->ModulationSpeed << 6); + chan->ModulationCount2 &= 0x00FF; + chan->ModulationCount2 |= (cnt << 8); + } + } + + return (int)modfactor; +} + +u16 CalcVolume(int vol) +{ + if (vol < -723) vol = -723; + else if (vol > 0) vol = 0; + + u8 ret = SWIVolumeTable[vol + 723]; + + int div = 0; + if (vol < -240) div = 3; + else if (vol < -120) div = 2; + else if (vol < -60) div = 1; + + return (div << 8) | ret; +} + +u16 CalcFreq(u32 unk, int freq) +{ + freq = -freq; + + int div = 0; + while (freq < 0) + { + div--; + freq += 768; + } + while (freq >= 768) + { + div++; + freq -= 768; + } + + u64 pitch = SWIPitchTable[freq] + 0x10000; + pitch *= unk; + + div -= 16; + if (div <= 0) + { + pitch >>= (-div); + } + else if (div < 32) + { + pitch <<= div; + // CHECKME + if (!pitch) return 0xFFFF; + } + else + { + return 0xFFFF; + } + + if (pitch < 0x10) + pitch = 0x10; + else if (pitch > 0xFFFF) + pitch = 0xFFFF; + + return (u16)(pitch & 0xFFFF); +} + +void UpdateChannels(bool updateramps) +{ + for (int i = 0; i < 16; i++) + { + Channel* chan = &Channels[i]; + if (!(chan->StatusFlags & (1<<0))) continue; + + if (chan->StatusFlags & (1<<1)) + { + chan->StatusFlags |= (1<<3); + chan->StatusFlags &= ~(1<<1); + } + else if (!IsChannelPlaying(i)) + { + if (chan->Linked) + UnlinkChannel(chan, true); + else + chan->Priority = 0; + + chan->Volume = 0; + chan->VolumeDiv = 0; + chan->StatusFlags &= ~(1<<0); + continue; + } + + int vol = BaseVolumeTable[chan->VolBase2 & 0x7F]; + int freq = (chan->FreqBase2 - chan->FreqBase1) << 6; + int pan = 0; + + vol += ChannelVolumeRamp(chan, updateramps); + freq += ChannelFreqRamp(chan, updateramps); + + vol += chan->VolBase3; + vol += chan->VolBase1; + freq += chan->FreqBase3; + + int mod = ChannelModulation(chan, updateramps); + switch (chan->ModulationType) + { + case 0: + freq += mod; + break; + case 1: + if (vol > -0x8000) + vol += mod; + break; + case 2: + pan += mod; + break; + } + + if (chan->PanBase1 != 0x7F) + { + pan = ((pan * (int)chan->PanBase1) + 64) >> 7; + } + pan += chan->PanBase3; + + if ((chan->VolRampPhase == 3) && (vol <= -0x2D3)) + { + chan->StatusFlags &= ~0xF8; + chan->StatusFlags |= (1<<4); + + if (chan->Linked) + UnlinkChannel(chan, true); + else + chan->Priority = 0; + + chan->Volume = 0; + chan->VolumeDiv = 0; + chan->StatusFlags &= ~(1<<0); + continue; + } + + u16 finalvol = CalcVolume(vol); + + u16 finalfreq = CalcFreq(chan->SwavFrequency, freq); + if (chan->Type == 1) finalfreq &= 0xFFFC; + + pan += 64; + if (pan < 0) pan = 0; + else if (pan > 127) pan = 127; + u8 finalpan = (u8)pan; + + if (finalvol != (chan->Volume | (chan->VolumeDiv<<8))) + { + chan->Volume = finalvol & 0xFF; + chan->VolumeDiv = finalvol >> 8; + chan->StatusFlags |= (1<<6); + } + + if (finalfreq != chan->Frequency) + { + chan->Frequency = finalfreq; + chan->StatusFlags |= (1<<5); + } + + if (finalpan != chan->Pan) + { + chan->Pan = finalpan; + chan->StatusFlags |= (1<<7); + } + } +} + + +void ReportHardwareStatus() +{ + if (!SharedMem) return; + + u16 chanmask = 0; + for (int i = 0; i < 16; i++) + { + if (IsChannelPlaying(i)) + chanmask |= (1<> 16); +} + + +void Process(u32 param) +{ + if (param) + { + NDS::ScheduleEvent(NDS::Event_HLE_SoundCmd, true, 174592, Process, 1); + } + + UpdateHardwareChannels(); + ProcessCommands(); + ProcessSequences(param!=0); + UpdateChannels(param!=0); + ReportHardwareStatus(); + UpdateCounter(); +} + + +void OnIPCRequest(u32 data) +{ + if (data == 0) + { + // process pending command buffers + + Process(0); + } + else if (data >= 0x02000000) + { + // receiving a command buffer, add it to the queue + + CmdQueue.Write(data); + } +} + +} +} +} diff --git a/src/HLE_Retail/Sound_Nitro.h b/src/HLE_Retail/Sound_Nitro.h new file mode 100644 index 00000000..35b12c61 --- /dev/null +++ b/src/HLE_Retail/Sound_Nitro.h @@ -0,0 +1,42 @@ +/* + Copyright 2016-2022 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 SOUND_NITRO_H +#define SOUND_NITRO_H + +#include "../types.h" + +namespace HLE +{ +namespace Retail +{ +namespace Sound_Nitro +{ + +void Reset(); + +void OnAlarm(u32 param); +void Process(u32 param); + +void OnIPCRequest(u32 data); + +} +} +} + +#endif // SOUND_NITRO_H diff --git a/src/HLE_Retail/Sound_Peach.cpp b/src/HLE_Retail/Sound_Peach.cpp new file mode 100644 index 00000000..e62aac80 --- /dev/null +++ b/src/HLE_Retail/Sound_Peach.cpp @@ -0,0 +1,1887 @@ +/* + Copyright 2016-2022 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 "../NDS.h" +#include "../NDSCart.h" +#include "../HLE.h" +#include "../SPU.h" + +#include "IPC.h" +#include "Sound_Peach.h" + + +namespace HLE +{ +namespace Retail +{ +namespace Sound_Peach +{ + +struct Note +{ + u32 NoteUnk00; + u8 Volume; + s8 NoteUnk05; + u8 Pan; + u8 NoteUnk07; + u8 NoteUnk08; + u8 NoteUnk09; + u16 NoteUnk0A; + u16 NoteUnk0C; + u16 NoteUnk0E; + u32 DataAddr; + u32 NoteUnk14; +}; + +struct Sequence +{ + u16 ID; + u8 SeqUnk02; + u8 SeqUnk03; + u8 Flags; + u8 Volume; + s8 SeqUnk06; + s8 SeqUnk07; + u8 VolRampLen1; + u8 VolRampPos1; + u8 VolRampLen2; + u8 VolRampPos2; + u8 VolMode1; + u8 VolMode2; + u8 Volume1; + u8 Volume2; + u8 SeqUnk10; + u8 MaxNotes; + u8 SeqUnk12; + s8 SeqUnk13; + Note* NoteBuffer; + u32 SoundDataBuffer; +}; + +struct Channel +{ + u32 SourceData; + Note* CurNote; + Sequence* CurSequence; + u8 CurVolume; + u8 CurPan; + s16 ChanUnk0E; + u8 BaseVolume; + u8 Status; + u8 BasePan; + u8 ID; // hardware channel # + u8 ChanUnk14; + s8 ChanUnk15; + u16 ChanUnk16; + u8 VolPhase; + u8 VolStep; + u8 PanPhase; + u8 PanStep; + u8 VolLength; + u8 PanLength; + u16 ChanUnk1E; +}; + +const u16 FMTable[97] = +{ + 0xFFFF, + 0xFE29, 0xFC54, 0xFA84, 0xF8B6, 0xF6EC, 0xF525, 0xF362, 0xF1A2, + 0xEFE5, 0xEE2B, 0xEC74, 0xEAC1, 0xE910, 0xE763, 0xE5B9, 0xE412, + 0xE26E, 0xE0CD, 0xDF2F, 0xDD94, 0xDBFC, 0xDA67, 0xD8D4, 0xD745, + 0xD5B9, 0xD42F, 0xD2A8, 0xD124, 0xCFA3, 0xCE25, 0xCCA9, 0xCB30, + 0xC9BA, 0xC846, 0xC6D5, 0xC567, 0xC3FC, 0xC293, 0xC12C, 0xBFC9, + 0xBE67, 0xBD09, 0xBBAC, 0xBA53, 0xB8FC, 0xB7A7, 0xB655, 0xB505, + 0xB3B8, 0xB26D, 0xB124, 0xAFDE, 0xAE9A, 0xAD58, 0xAC19, 0xAADC, + 0xA9A1, 0xA869, 0xA733, 0xA5FF, 0xA4CD, 0xA39E, 0xA270, 0xA145, + 0xA01C, 0x9EF5, 0x9DD0, 0x9CAE, 0x9B8D, 0x9A6F, 0x9952, 0x9838, + 0x9720, 0x9609, 0x94F5, 0x93E3, 0x92D2, 0x91C4, 0x90B7, 0x8FAD, + 0x8EA4, 0x8D9E, 0x8C99, 0x8B96, 0x8A95, 0x8995, 0x8898, 0x879C, + 0x86A3, 0x85AB, 0x84B5, 0x83C0, 0x82CE, 0x81DD, 0x80ED, 0x8000 +}; + +bool Inited; +u32 SharedMem; +u32 IPCCmd; +u32 CaptureVolume; + +u32 SoundBuffer[2]; + +Note NoteBuffer[64]; +Sequence Sequences[16]; +Channel Channels[16]; +const int NumSequences = 16; +int NumChannels; +int NumPSGChannels, NumNoiseChannels; + +u32 CommandBuffers[2][0x40]; +int CurCommandBuffer; + +u32 Cmd3Val; +u32 Cmd5Val; +u32 Cmd10Val; + +u16 Stat[2]; + + +void Reset() +{ + Inited = false; + SharedMem = 0; + IPCCmd = 0; + CaptureVolume = 0; + + SoundBuffer[0] = 0; + SoundBuffer[1] = 0; + + memset(NoteBuffer, 0, sizeof(NoteBuffer)); + memset(Sequences, 0, sizeof(Sequences)); + memset(Channels, 0, sizeof(Channels)); + NumChannels = 16; + NumPSGChannels = 0; + NumNoiseChannels = 0; + + memset(CommandBuffers, 0, sizeof(CommandBuffers)); + CurCommandBuffer = 0; + + Cmd3Val = 0; + Cmd5Val = 0; + Cmd10Val = 0; + + Stat[0] = 0; + Stat[1] = 0; + + NDS::ScheduleEvent(NDS::Event_HLE_SoundCmd, true, 262144, Process, 0); +} + + +void ResetChannel(Channel* chan) +{ + u32 addr = 0x04000400 + (chan->ID * 0x10); + + SPU::Write32(addr+0x00, 0); + SPU::Write16(addr+0x08, 0); + SPU::Write32(addr+0x04, 0); + SPU::Write32(addr+0x0C, 0); + SPU::Write16(addr+0x0A, 0); +} + + +void RunSequenceCommand(Sequence* seq, u32 seqentry, int type); + +void CmdDummy(u32 param) {} + +void PlaySequence(u32 param) +{ + u32 seqid = param >> 22; + u32 val = (param >> 16) & 0x3F; + + if (val != 0 && seqid == 0x1E) + { + u16 modul = FMTable[Stat[0]]; + seqid += (modul & 0x3); + + Stat[0]++; + if (Stat[0] > 96) + Stat[0] = 0; + } + + if (!seqid) return; + + u32 soundbuf = SoundBuffer[val ? 1:0]; + if (seqid > NDS::ARM7Read32(soundbuf + 0x10)) return; + + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqentry = seqtable + ((seqid-1) * 8); + u32 seqdata = NDS::ARM7Read32(seqentry); + u32 seqflags = NDS::ARM7Read32(seqentry + 0x4); + + if (!seqdata) return; + seqdata += soundbuf; + + Sequence* seq; + u32 sharedmem; + + if (val == 9 && seqid > 0x96 && seqid < 0x9F) + { + printf("BIKI\n"); + // TODO!! + } + else + { + u32 seqnum = seqflags & 0xF; + seq = &Sequences[seqnum]; + sharedmem = SharedMem + 0x87C00 + (seqnum * 16); + } + + u32 tmp = (seqflags >> 14) & 0x3F; + if (tmp == 0x3F) + { + u32 chk = (seqflags >> 11) & 0x7; + if (((seq->Flags & 0xF) != 0) && (seq->SeqUnk10 >= chk)) + return; + } + else + { + u32 chk = (seqflags >> 11) & 0x7; + if (((seq->Flags & 0xF) != 0) && (seq->SeqUnk10 > chk)) + return; + } + + tmp = (seq->Flags & 0xF) - 1; + if (tmp <= 1) + { + seq->Flags &= ~0xF; + u32 oldoffset = seqtable + ((seq->ID-1) * 8); + RunSequenceCommand(seq, oldoffset, 1); + } + + for (int i = 0; i < NumChannels; i++) + { + Channel* chan = &Channels[i]; + + if (chan->CurSequence == seq) + chan->Status = 0; + } + + memset(seq->NoteBuffer, 0, sizeof(Note)*seq->MaxNotes); + + u32 numnotes = NDS::ARM7Read32(seqdata); + if (numnotes > seq->MaxNotes) + numnotes = seq->MaxNotes; + + for (int i = 0; i < numnotes; i++) + { + Note* note = &seq->NoteBuffer[i]; + + note->DataAddr = soundbuf + NDS::ARM7Read32(seqdata + 4 + (i*4)); + note->Volume = 0x7F; + note->Pan = 0x3F; + } + + seq->ID = 0; + seq->SeqUnk02 = 0; + seq->SeqUnk03 = 0; + seq->Flags = 0; + seq->Volume = 0; + seq->SeqUnk06 = 0; + seq->SeqUnk07 = 0; + seq->VolRampLen1 = 0; + seq->VolRampPos1 = 0; + seq->VolRampLen2 = 0; + seq->VolRampPos2 = 0; + seq->VolMode1 = 0; + seq->VolMode2 = 0; + seq->Volume1 = 0; + seq->Volume2 = 0; + + seq->SeqUnk10 = (seqflags >> 11) & 0x7; + seq->ID = seqid; + seq->Flags |= 0x1; + seq->Volume1 = (seqflags >> 4) & 0x7F; + seq->SeqUnk02 = ((seqflags >> 14) & 0x3F) << 1; + seq->VolMode1 = 2; + seq->VolRampLen1 = (param >> 8) & 0xFF; + seq->VolRampPos1 = (seq->Volume * seq->VolRampLen1) / seq->Volume1; + seq->SeqUnk12 = val; + seq->SoundDataBuffer = soundbuf; + seq->SeqUnk13 = 0; + if (seq->SeqUnk02 == 0x7E) seq->SeqUnk02 = 0; + + NDS::ARM7Write16(sharedmem + 0x2, seq->ID); + NDS::ARM7Write16(sharedmem + 0xE, (seqflags >> 4) & 0x7F); + NDS::ARM7Write16(sharedmem + 0x8, seq->SeqUnk10); + NDS::ARM7Write16(sharedmem + 0x4, seq->SeqUnk12); + + if (seq == &Sequences[0]) + { + Cmd5Val = 0; + } + + RunSequenceCommand(seq, seqentry, 0); +} + +void Cmd3(u32 param) +{ + u32 seqid = param >> 22; + u32 val = (param >> 16) & 0x3F; + + if (val == 9 && seqid > 0x96 && seqid < 0x9F) + {printf("BOUF\n"); + for (int i = 4; i < 7; i++) + { + Sequence* seq = &Sequences[i]; + seq->VolMode2 = 0; + seq->VolRampLen2 = 0; + seq->VolRampPos2 = 0; + seq->VolMode1 = 1; + seq->VolRampLen1 = 0; + seq->VolRampPos1 = 0; + } + + return; + } + + u32 soundbuf = SoundBuffer[val ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqentry = seqtable + ((seqid-1) * 8); + u32 seqflags = NDS::ARM7Read32(seqentry + 0x4); + + u32 nseq = seqflags & 0xF; + Sequence* seq = &Sequences[nseq]; + if ((seq->Flags & 0xF) == 0) return; + if (seq->SeqUnk12 != val) return; + if (seq->ID != seqid) return; + + seq->VolMode2 = 0; + seq->VolRampLen2 = 0; + seq->VolRampPos2 = 0; + seq->VolMode1 = 1; + seq->VolRampLen1 = (param >> 8) & 0xFF; + + if (seq->Volume1 == 0) + seq->VolRampPos1 = (-seq->Volume * seq->VolRampLen1); + else + seq->VolRampPos1 = (-seq->Volume * seq->VolRampLen1) / seq->Volume1; + + RunSequenceCommand(seq, seqentry, 1); +} + +void Cmd4(u32 param) +{ + u32 seqid = param >> 22; + u32 val = (param >> 16) & 0x3F; + + u32 soundbuf = SoundBuffer[val ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqentry = seqtable + ((seqid-1) * 8); + u32 seqflags = NDS::ARM7Read32(seqentry + 0x4); + + u32 nseq = seqflags & 0xF; + Sequence* seq = &Sequences[nseq]; + if ((seq->Flags & 0xF) != 1) return; + if (seq->SeqUnk12 != val) return; + if (seq->ID != seqid) return; + if (seq->VolMode2 == 1) return; + + if (seq->VolMode2 == 0) + { + seq->Volume2 = seq->Volume; + } + + seq->VolMode2 = 1; + seq->VolRampLen2 = (param >> 8) & 0xFF; + if (seq->Volume2 == 0) + seq->VolRampPos2 = (seq->VolRampLen2 * (-seq->Volume)); + else + seq->VolRampPos2 = (seq->VolRampLen2 * (-seq->Volume)) / seq->Volume2; +} + +void Cmd5(u32 param) +{ + u32 seqid = param >> 22; + u32 val = (param >> 16) & 0x3F; + + u32 soundbuf = SoundBuffer[val ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqentry = seqtable + ((seqid-1) * 8); + u32 seqflags = NDS::ARM7Read32(seqentry + 0x4); + + u32 nseq = seqflags & 0xF; + Sequence* seq = &Sequences[nseq]; + if (seq->SeqUnk12 != val) return; + if (seq->ID != seqid) return; + if (seq->VolMode2 == 2 || seq->VolMode2 == 0) return; + + seq->Flags &= ~0xF; + seq->Flags |= 0x1; + + seq->VolMode2 = 2; + seq->VolRampLen2 = (param >> 8) & 0xFF; + if (seq->Volume2 == 0) + seq->VolRampPos2 = (seq->VolRampLen2 * seq->Volume); + else + seq->VolRampPos2 = (seq->VolRampLen2 * seq->Volume) / seq->Volume2; +} + +void Cmd7(u32 param) +{ + u16 mask = param >> 16; + + for (int i = 0; i < NumSequences; i++) + { + if (!(mask & (1<ID << 22; + cmd3param |= (param & 0xFF00); + cmd3param |= (seq->SeqUnk12 << 16); + Cmd3(cmd3param); + } +} + +void SeqSetVolume(u32 param) +{ + u8 val = (param >> 8) & 0xFF; + u16 mask = param >> 16; + + for (int i = 0; i < NumSequences; i++) + { + if (mask & (1<> 8) & 0xFF; + u16 mask = param >> 16; + + for (int i = 0; i < NumSequences; i++) + { + if (mask & (1<> 8) & 0xFF; + u16 mask = param >> 16; + + for (int i = 0; i < NumSequences; i++) + { + if (mask & (1<> 8) & 0xFF; + u16 mask = param >> 16; + + for (int i = 0; i < NumSequences; i++) + { + if (mask & (1<> 8) & 0xFF; + u32 num_noise = (param >> 16) & 0xFF; + u32 ch = 0; + + printf("Sound_Peach: setting up channels, param=%08X\n", param); + + NumPSGChannels = num_psg; + NumNoiseChannels = num_noise; + + for (u32 i = 0; i < num_psg; i++) + { + Channel* chan = &Channels[ch]; + chan->ID = 8 + i; + chan->Status = 0; + ch++; + } + + for (u32 i = 0; i < num_noise; i++) + { + Channel* chan = &Channels[ch]; + chan->ID = 14 + i; + chan->Status = 0; + ch++; + } + + { + Channel* chan = &Channels[ch]; + chan->ID = 0; + chan->Status = 0; + ch++; + + chan = &Channels[ch]; + chan->ID = 2; + chan->Status = 0; + ch++; + } + + for (u32 i = 0; i < 4; i++) + { + Channel* chan = &Channels[ch]; + chan->ID = 4 + i; + chan->Status = 0; + ch++; + } + + for (u32 i = num_psg; i < 6; i++) + { + Channel* chan = &Channels[ch]; + chan->ID = 8 + i; + chan->Status = 0; + ch++; + } + + for (u32 i = num_noise; i < 2; i++) + { + Channel* chan = &Channels[ch]; + chan->ID = 14 + i; + chan->Status = 0; + ch++; + } + + { + Channel* chan = &Channels[ch]; + chan->ID = 1; + chan->Status = 0; + ch++; + + chan = &Channels[ch]; + chan->ID = 3; + chan->Status = 0; + ch++; + } +} + +void SetupCapture(u32 param) +{ + u32 buf0 = SharedMem + 0x83C00; + u32 buf1 = SharedMem + 0x85C00; + + printf("Sound_Peach: setting up capture, param=%08X\n", param); + + u32 volume = (param >> 16) & 0xFF; + + if (CaptureVolume && (!volume)) + { + SPU::Write16(0x04000508, 0); + NumChannels = 16; + } + + if (volume && (!CaptureVolume)) + { + for (u32 i = 0; i < 0x4000; i+=4) + NDS::ARM7Write32(buf0+i, 0); + + NumChannels = 14; + + ResetChannel(&Channels[14]); + ResetChannel(&Channels[15]); + + SPU::Write32(0x04000510, buf0); + SPU::Write32(0x04000518, buf1); + SPU::Write16(0x04000514, 0x600); + SPU::Write16(0x0400051C, 0x600); + + SPU::Write32(0x04000414, buf0); + SPU::Write32(0x04000434, buf1); + SPU::Write32(0x0400041C, 0x600); + SPU::Write32(0x0400043C, 0x600); + SPU::Write16(0x0400041A, 0); + SPU::Write16(0x0400043A, 0); + SPU::Write16(0x04000418, 0xFC00); + SPU::Write16(0x04000438, 0xFC00); + SPU::Write8(0x04000412, 0x00); + SPU::Write8(0x04000432, 0x7F); + SPU::Write8(0x04000413, 0xA8); + SPU::Write8(0x04000433, 0xA8); + + SPU::Write16(0x04000508, 0x8080); + + SPU::Write16(0x04000410, volume); + SPU::Write16(0x04000430, volume); + } + + CaptureVolume = volume; +} + +void CmdUnk(u32 param) +{ + printf("PEACH UNK CMD %08X\n", param); +} + +void (*CommandFuncs[16])(u32) = +{ + CmdDummy, + PlaySequence, + Cmd3, + Cmd4, + Cmd5, + CmdUnk, + Cmd7, + CmdUnk, + CmdUnk, + CmdUnk, + SeqSetVolume, + SeqSetUnk06, + SeqSetUnk07, + SeqSetUnk13, + SetupChannels, + SetupCapture, +}; + + +void RunSequenceCommand(Sequence* seq, u32 seqentry, int type) +{ + u32 offset = seq->SoundDataBuffer + 0xA54; + u32 seqflags = NDS::ARM7Read32(seqentry + 0x4); + + int start; + if (type) start = (seqflags >> 26) - 1; + else start = ((seqflags >> 20) & 0x3F) - 1; + if (start < 0) return; + + u32 val = NDS::ARM7Read32(offset + (start*4)); + u32 cmd = val & 0xFF; + if (cmd < 1 || cmd > 16) return; + + if (cmd >= 2 && cmd <= 5) + { + u32 tmp = (val >> 16) & 0x3F; + if (tmp && tmp != Cmd3Val) + return; + } + + cmd--; + CommandFuncs[cmd](val); +} + +void RunCommands() +{ + u32* cmd = CommandBuffers[CurCommandBuffer]; + for (u32 i = 0; i < 0x40; i++) + { + u32 val = cmd[i]; + u8 cmdid = val & 0xFF; + if (cmdid < 1 || cmdid > 16) break; + + cmdid--; + CommandFuncs[cmdid](val); + } + + cmd[0] = 0; + + SendIPCReply(0x7, 2); + SendIPCReply(0x7, 0x10001); +} + + +void UpdateSequenceVolume(Sequence* seq) +{ + if (seq->VolMode2 == 1) + { + if (seq->Volume == 0) return; + + u8 vol = 0; + if (seq->VolRampLen2 != 0) + { + u8 pos = seq->VolRampPos2; + seq->VolRampPos2++; + + vol = (seq->Volume2 * (seq->VolRampLen2 - pos)) / seq->VolRampLen2; + } + if (vol == 0) + { + for (int i = 0; i < NumChannels; i++) + { + Channel* chan = &Channels[i]; + if (chan->CurSequence == seq) + chan->Status = 0; + } + + seq->Flags &= ~0xF; + seq->Flags |= 0x2; + } + seq->Volume = vol; + } + else if (seq->VolMode2 == 2) + { + if (seq->VolRampLen2 == 0) + { + seq->Volume = seq->Volume2; + } + else + { + u8 pos = seq->VolRampPos2; + seq->VolRampPos2++; + + seq->Volume = (seq->Volume2 * pos) / seq->VolRampLen2; + } + + if (seq->Volume < seq->Volume2) return; + + if (!seq->SeqUnk03) + { + seq->Volume = seq->Volume2; + seq->VolMode2 = 0; + return; + } + + seq->Volume2 = seq->Volume1; + seq->VolRampLen2 = seq->SeqUnk03; + seq->VolMode2 = 3; + seq->SeqUnk03 = 0; + } + else if (seq->VolMode2 == 3) + { + if (seq->VolMode1 != 0) + { + seq->VolMode2 = 0; + return; + } + + seq->Volume = (seq->Volume1 * seq->VolRampLen2) / 128; + } + else if (seq->VolMode1 == 1) + { + u8 vol = 0; + if (seq->VolRampLen1 != 0) + { + u8 pos = seq->VolRampPos1; + seq->VolRampPos1++; + + vol = (seq->Volume1 * (seq->VolRampLen1 - pos)) / seq->VolRampLen1; + } + if (vol == 0) + { + seq->VolMode1 = 0; + + for (int i = 0; i < seq->MaxNotes; i++) + { + seq->NoteBuffer[i].DataAddr = 0; + } + + for (int i = 0; i < NumChannels; i++) + { + Channel* chan = &Channels[i]; + if (chan->CurSequence == seq) + chan->Status = 0; + } + + seq->Flags &= ~0xF; + } + seq->Volume = vol; + } + else if (seq->VolMode1 == 2) + { + if (seq->VolRampLen1 == 0) + { + seq->Volume = seq->Volume1; + } + else + { + u8 pos = seq->VolRampPos1; + seq->VolRampPos1++; + + seq->Volume = (seq->Volume1 * pos) / seq->VolRampLen1; + } + + if (seq->Volume == seq->Volume1) + { + seq->VolMode1 = 0; + } + } +} + +int NoteFM(Sequence* seq, Note* note) +{ + int num = seq->SeqUnk07 + 96; + int div = num / 96; + int mod = num % 96; + + int val = note->NoteUnk0A * FMTable[96 - mod]; + div = 16 - div; + val >>= div; + + int div2 = val ? (160 / val) : 160; + int coarse = div2 * val; + coarse = 160 - coarse; + + for (;;) + { + note->NoteUnk0C += coarse; + if ((int)note->NoteUnk0C >= val) + { + div2++; + note->NoteUnk0C -= val; + } + + note->NoteUnk09--; + if (note->NoteUnk09 == 0) break; + if (div2 != 0) break; + } + + note->NoteUnk0E = div2 & 0xFF; + return div2 & 0xFFFF; +} + +void ChannelFM(Channel* chan) +{ + Note* note = chan->CurNote; + Sequence* seq = chan->CurSequence; + int unk0C = note->NoteUnk0C; + + int num = seq->SeqUnk07 + 96; + int div = num / 96; + int mod = num % 96; + + int val = note->NoteUnk0A * FMTable[96 - mod]; + div = 16 - div; + val >>= div; + + if (chan->ChanUnk16 == 0) + { + if (chan->VolPhase == 0) return; + if (chan->VolPhase == 5) return; + chan->Status = 2; + return; + } + + int div2 = val ? (160 / val) : 160; + int coarse = div2 * val; + coarse = 160 - coarse; + + for (;;) + { + unk0C += coarse; + if (unk0C >= val) + { + div2++; + unk0C -= val; + } + + chan->ChanUnk16--; + if (chan->ChanUnk16 == 0) break; + if (div2 != 0) break; + } + + if (div2 == 0) + chan->Status = 2; + + chan->ChanUnk14 = div2 & 0xFF; +} + +void ProcessNotes(Sequence* seq) +{ + if (seq->SeqUnk02 != 0) + { + seq->SeqUnk02--; + return; + } + + if (seq->MaxNotes == 0) + { + u32 soundbuf = SoundBuffer[seq->SeqUnk12 ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqoffset = seqtable + ((seq->ID-1) * 8); + + seq->Flags &= ~0xF; + + RunSequenceCommand(seq, seqoffset, 1); + return; + } + + int firstchan = 0; + int nprocessed = 0; + int r10 = -1; + u32 r4 = 0; + + for (int i = 0; i < seq->MaxNotes; i++) + { + r10++; + + Note* note = &seq->NoteBuffer[i]; + u32 note0 = note->NoteUnk00; + u32 note8 = NDS::ARM7Read32(note0 + 8); + if (!note->DataAddr) continue; + + r4 = (16 - nprocessed) + ((note->NoteUnk08 & 0x3) << 10) + (seq->SeqUnk10 << 5); + + nprocessed++; + + if (note->NoteUnk0E) + { + if (((note->NoteUnk08 >> 2) & 0x3) == 0) + note->NoteUnk0E--; + + continue; + } + else if (note->NoteUnk09 != 0) + { + if (NoteFM(seq, note) != 0) + { + if (((note->NoteUnk08 >> 2) & 0x3) == 0) + note->NoteUnk0E--; + + continue; + } + } + + u32 notedata = note->DataAddr; + bool note_end = false; + for (;;) + { + u16 data = NDS::ARM7Read16(notedata); + u8 cmd = data & 0xFF; + u8 param = data >> 8; + + if ((cmd >= 0xF0) && (!note_end)) + { + notedata += 2; + + switch (cmd) + { + case 0xF0: + note->NoteUnk0A = param; + break; + case 0xF1: + { + for (int j = 0; j < NumChannels; j++) + { + Channel* chan = &Channels[j]; + if (chan->CurNote == note) + chan->Status = 0; + } + + u32 soundbuf; + u32 index = note->NoteUnk07; + if (index < 0x40) + { + soundbuf = SoundBuffer[0]; + } + else + { + soundbuf = SoundBuffer[1]; + index -= 0x40; + } + + u32 offset = NDS::ARM7Read32(soundbuf + 0x4); + + u32 tmp = param >> 1; + tmp += (index*64); + tmp = NDS::ARM7Read16(soundbuf + 0x14 + (tmp*2)); + + if (param & 0x01) + tmp >>= 8; + else + tmp &= 0xFF; + + s32 stmp = ((s32)(tmp << 24)) >> 24; + if (stmp == -1) + { + SendIPCReply(0x7, 3); + SendIPCReply(0x7, 0x10001); + + note->NoteUnk00 = 0; + note0 = 0; + break; + } + + u32 noteaddr = soundbuf + NDS::ARM7Read32(soundbuf + offset + (index*4)); + noteaddr += (stmp * 12); + note->NoteUnk00 = noteaddr; + note0 = noteaddr; + } + break; + case 0xF2: + note->NoteUnk07 = param; + break; + case 0xF3: + note->Pan = param; + break; + case 0xF4: + note->NoteUnk08 &= ~0xC; + note->NoteUnk08 |= 0x4; + break; + case 0xF5: + note->NoteUnk05 = param; + break; + case 0xF6: + note->Volume = param; + break; + case 0xF8: + note->NoteUnk14 = notedata; + break; + case 0xF9: + notedata = note->NoteUnk14; + break; + case 0xFA: + note->NoteUnk08 &= ~0x3; + note->NoteUnk08 |= 0x1; + break; + case 0xFB: + note->NoteUnk08 &= ~0x3; + break; + case 0xFC: + { + if (seq != &Sequences[0]) break; + if (Cmd10Val == 0) break; + + u32 soundbuf = SoundBuffer[seq->SeqUnk12 ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqoffset = seqtable + ((param-1) * 8); + + u32 seqdata = soundbuf + NDS::ARM7Read32(seqoffset); + seqdata += (r10*4); + notedata = soundbuf + NDS::ARM7Read32(seqdata+4); + } + break; + case 0xFD: + { + if (seq != &Sequences[0]) break; + if (Cmd10Val != 0) break; + + u32 soundbuf = SoundBuffer[seq->SeqUnk12 ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + u32 seqoffset = seqtable + ((param-1) * 8); + + u32 seqdata = soundbuf + NDS::ARM7Read32(seqoffset); + seqdata += (r10*4); + notedata = soundbuf + NDS::ARM7Read32(seqdata+4); + } + break; + case 0xFF: + note->DataAddr = 0; // checkme + note_end = true; + break; + } + } + else + { + if (note_end) break; + +haxloop: + data = NDS::ARM7Read16(notedata); + if ((data & 0xFF) > 0x7F) + { +haxloop2: + u16 param = NDS::ARM7Read16(notedata); + notedata += 2; + if ((param & 0xFF) == 0) continue; + + note->NoteUnk09 = param >> 8; + note->NoteUnk0E = 0; + + if (NoteFM(seq, note) == 0) + continue; + + note->DataAddr = notedata; + if (((note->NoteUnk08 >> 2) & 0x3) == 0) + note->NoteUnk0E--; + + break; + } + else + { + if (!note0) break; + + note8 = NDS::ARM7Read32(note->NoteUnk00 + 8); + if ((note8 & 0x7) == 7) + { + int idx = data & 0xFF; + idx -= 0x24; + if (idx < 0 || idx > 0x47) + { + SendIPCReply(0x7, 3); + SendIPCReply(0x7, 0x10001); + break; + } + + int tmp = (note8 >> 3) & 0x7F; + u32 soundbuf; + if (tmp < 0x40) + { + soundbuf = SoundBuffer[0]; + } + else + { + soundbuf = SoundBuffer[1]; + tmp -= 0x40; + } + + u32 offset = soundbuf + NDS::ARM7Read32(soundbuf + 0x8); + u32 tmp2 = (tmp * 0x24) + (idx >> 1); + u16 tmp3 = NDS::ARM7Read16(soundbuf + 0x814 + (tmp2*2)); + + if (idx & 0x1) + tmp3 >>= 8; + else + tmp3 &= 0xFF; + + s32 stmp = ((s32)(tmp3 << 24)) >> 24; + if (stmp == -1) + { + SendIPCReply(0x7, 3); + SendIPCReply(0x7, 0x10001); + break; + } + + u32 tmpoffset = soundbuf + NDS::ARM7Read32(offset + (tmp*4)); + tmpoffset += (tmp3 * 12); + note0 = tmpoffset; + note8 = NDS::ARM7Read32(tmpoffset + 8); + } + + int lastchan = 0; + int chtype = (note8 & 0x7); +ch_again: + switch (chtype) + { + case 1: + firstchan = 0; + lastchan = NumPSGChannels; + r4 += 0x8000; + break; + case 2: + firstchan = NumPSGChannels; + lastchan = firstchan + NumNoiseChannels; + r4 += 0x8000; + break; + case 3: + firstchan = NumPSGChannels + NumNoiseChannels; + lastchan = NumChannels; + break; + case 8: + firstchan = 0; + lastchan = NumPSGChannels + NumNoiseChannels; + break; + default: + printf("???? %d\n", chtype); + break; + } + + // allocate a channel + Channel* chan = nullptr; + for (int j = firstchan; j < lastchan; j++) + { + Channel* ch = &Channels[j]; + if (ch->Status != 0) continue; + chan = ch; + break; + } + if (!chan) + { + for (int j = firstchan; j < lastchan; j++) + { + Channel* ch = &Channels[j]; + if (ch->Status >= 3) continue; + chan = ch; + break; + } + } + if (!chan) + { + u16 chk = 0xFFFF; + chan = &Channels[firstchan]; + for (int j = firstchan; j < lastchan; j++) + { + Channel* ch = &Channels[j]; + if (ch->ChanUnk1E < chk) + { + chan = ch; + chk = ch->ChanUnk1E; + } + } + + if ((firstchan == lastchan) || (r4 < chan->ChanUnk1E)) + { + if (chtype == 3) + {//printf("type3 channel alloc fail (%d->%d)\n", firstchan, lastchan); + //for (int j = 0; j < 16; j++) printf("%d:%d/%04X ", j, Channels[j].Status, Channels[j].ChanUnk1E); printf("\n"); + //printf("tried to go for: unk1E=%04X, r4=%04X\n", chan->ChanUnk1E, r4); + chtype = 8; + goto ch_again; + } +printf("channel alloc: fail (type=%d)\n", chtype); + goto next_cmd; + } + } + + chan->CurNote = note; + chan->CurSequence = seq; + chan->ChanUnk1E = r4; + chan->Status = 4; + chan->ChanUnk15 = data & 0xFF; + chan->BaseVolume = data >> 8; + + u16 data2 = NDS::ARM7Read16(notedata+2); + chan->ChanUnk16 = data2; + chan->ChanUnk14 = 0; + ChannelFM(chan); + + notedata += 4; + goto haxloop; + } + +next_cmd: + do + { + data = NDS::ARM7Read16(notedata + 4); + notedata += 4; + } + while ((data & 0xFF) <= 0x7F); + goto haxloop2; + } + } + } + + if (!nprocessed) + { + u32 soundbuf = SoundBuffer[seq->SeqUnk12 ? 1:0]; + u32 seqtable = soundbuf + NDS::ARM7Read32(soundbuf + 0xC); + + seq->Flags &= ~0xF; + + u32 seqoffset = seqtable + ((seq->ID-1) * 8); + RunSequenceCommand(seq, seqoffset, 1); + } +} + + +u8 UpdateChannelVolume(Channel* chan, u32 seqdata) +{ + Note* note = chan->CurNote; + Sequence* seq = chan->CurSequence; + + u32 vol_param = NDS::ARM7Read32(seqdata + 4); + + u32 step_vol = (seq->Volume * chan->BaseVolume * note->Volume) >> 14; + u32 sus_vol = ((vol_param >> 19) & 0x3F) + 1; + sus_vol = (step_vol * sus_vol) >> 6; + + if (chan->VolPhase == 0) + { + chan->Status = 0; + return 0; + } + + if (chan->VolPhase == 1) + { + // attack + + if (chan->VolStep == 0) + { + chan->VolLength = (vol_param >> 5) & 0x7F; + } + + u8 step = chan->VolStep; + if (step == chan->VolLength) + { + chan->VolPhase++; + chan->VolStep = 0; + } + else + { + chan->VolStep++; + if (chan->VolLength == 0) return (step_vol * step); + return (step_vol * step) / chan->VolLength; + } + } + + if (chan->VolPhase == 2) + { + // decay + + if (chan->VolStep == 0) + { + chan->VolLength = (vol_param >> 12) & 0x7F; + } + + u8 step = chan->VolStep; + if (step == chan->VolLength) + { + chan->VolPhase++; + chan->VolStep = 0; + } + else + { + chan->VolStep++; + u32 ret; + if (chan->VolLength == 0) + ret = ((step_vol - sus_vol) * step); + else + ret = ((step_vol - sus_vol) * step) / chan->VolLength; + return step_vol - ret; + } + } + + if (chan->VolPhase == 3) + { + // sustain + + return sus_vol; + } + + if (chan->VolPhase == 5) + { + // release + + if (chan->VolStep == 0) + { + chan->PanLength = vol_param >> 25; + chan->VolLength = chan->PanLength; + } + + u8 step = chan->VolStep; + if (step == chan->VolLength) + { + chan->VolStep = 0; + chan->VolPhase = 0; + chan->Status = 0; + return 0; + } + else + { + chan->VolStep++; + u32 ret; + if (chan->VolLength == 0) + ret = (sus_vol * step); + else + ret = (sus_vol * step) / chan->VolLength; + return sus_vol - ret; + } + } + + return 0; +} + +s16 UpdateChannelPan(Channel* chan, u32 seqdata, u32 r2) +{ + Note* note = chan->CurNote; + Sequence* seq = chan->CurSequence; + + u32 vol_param = NDS::ARM7Read32(seqdata + 4); + int pan_param = NDS::ARM7Read32(seqdata + 8); + + u32 idx; + u32 soundbuf; + if ((pan_param & 0x7) == 7) + { + idx = (pan_param >> 3) & 0x7F; + soundbuf = SoundBuffer[(idx<0x40) ? 0:1]; + } + else + { + idx = note->NoteUnk07; + soundbuf = SoundBuffer[(idx<0x40) ? 0:1]; + } + + soundbuf += 0xB54; + u32 r4; + if ((pan_param & 0x7) == 3) + { + u16 val = NDS::ARM7Read16(chan->SourceData + 2); + r4 = (r2 - val) << 3; + } + else + { + r4 = (r2 - 0x40) << 3; + } + + int xx = seq->SeqUnk06 << 1; + int yy = vol_param & 0x1F; + int r1 = note->NoteUnk05 * yy; + xx += (r1 >> 4); + r4 += xx; + + int zz = (pan_param << 15) >> 25; + int var20; + if (zz < 0 || zz > 15) + { + var20 = 0; + } + else + { + var20 = NDS::ARM7Read32(soundbuf + (zz*4)); + } + + if (chan->PanPhase == 1) + { + // attack + + if (chan->PanStep == 0) + { + chan->BasePan = 0; + chan->PanLength = var20 & 0x3F; + + note->NoteUnk08 &= ~0xF0; + + if (Cmd5Val == 1) + { + Stat[0]++; + if (Stat[0] > 96) + Stat[0] = 0; + } + } + + u8 step = chan->PanStep; + if (step == chan->PanLength) + { + chan->PanPhase++; + chan->PanStep = 0; + } + else + { + chan->PanStep++; + + int factor = (var20 << 21) >> 27; + if (chan->PanLength == 0) + r4 += ((chan->PanLength - step) * factor); + else + r4 += (((chan->PanLength - step) * factor) / chan->PanLength); + goto ret; + } + } + + if (chan->PanPhase == 2) + { + // decay + + if (chan->PanStep == 0) + { + chan->BasePan = 0; + chan->PanLength = (var20 >> 11) & 0x3F; + } + + u8 step = chan->PanStep; + if (step == chan->PanLength) + { + chan->PanPhase++; + chan->PanStep = 0; + } + else + { + chan->PanStep++; + goto ret; + } + } + + if (chan->PanPhase == 3) + { + // sustain + + int n = (var20 >> 17) & 0xF; + if (n == 0) + { + chan->PanStep = 0; + chan->PanPhase = 0; + goto ret; + } + + u8 step = chan->PanStep; + if (step == n) + { + chan->BasePan++; + chan->BasePan &= 0x3; + chan->PanStep = 0; + } + + if ((chan->BasePan & 0x1) == 0) + { + step = chan->PanStep; + chan->PanStep++; + } + else + { + step = chan->PanStep; + chan->PanStep++; + step = n - step; + } + + int factor = (var20 >> 21) & 0x3F; + int ret; + if (n == 0) + ret = (factor * step); + else + ret = (factor * step) / n; + int tmp = 1 - (chan->BasePan & 0x2); + r4 += ret * tmp; + goto ret; + } + + if (chan->PanPhase == 5) + { + // release + + u8 step = chan->PanStep; + if (step == chan->PanLength) + { + chan->PanStep = 0; + chan->PanPhase = 0; + goto ret; + } + else + { + chan->PanStep++; + + int factor = var20 >> 27; + if (chan->PanLength == 0) + r4 += (step * factor); + else + r4 += ((step * factor) / chan->PanLength); + goto ret; + } + } + +ret: + if (seq == &Sequences[0]) + { + r4 += (s16)Stat[1]; + r4 += (note->NoteUnk08 >> 4); + } + + return (r4 << 16) >> 16; +} + +void UpdateChannelRegs(Channel* chan, u32 param, bool restart) +{ + int unk15 = chan->ChanUnk0E; + int lr; + if (unk15 < 0) + { + lr = 16; + while (unk15 < 0) + { + lr--; + unk15 += 96; + } + } + else + { + lr = 15; + while (unk15 >= 0) + { + lr++; + unk15 -= 96; + } + unk15 += 96; + } + + int freq; + if ((param & 0x3) == 3) + freq = NDS::ARM7Read16(chan->SourceData + 0x4); + else + freq = 0x18DA; + + u32 addr = 0x04000400 + (chan->ID * 16); + + SPU::Write8(addr+0x00, chan->CurVolume); + + freq *= FMTable[unk15]; + freq >>= lr; + freq = (0x10000 - freq) & 0xFFFC; + SPU::Write16(addr+0x08, freq); + + SPU::Write8(addr+0x02, chan->CurPan); + + if (!restart) return; + + SPU::Write8(addr+0x03, 0); + + if ((param & 0x3) != 3) + { + param = (param >> 3) & 0x7F; + param |= 0xE0; + SPU::Write8(addr+0x03, param); + return; + } + + SPU::Write32(addr+0x04, chan->SourceData + 0xA); + SPU::Write32(addr+0x0C, NDS::ARM7Read32(chan->SourceData + 0x8)); + SPU::Write16(addr+0x0A, NDS::ARM7Read16(chan->SourceData + 0x6)); + + u16 chanparam = NDS::ARM7Read16(chan->SourceData); + SPU::Write8(addr+0x03, (chanparam >> 8) | 0x80); +} + +void UpdateChannel(Channel* chan) +{ + u32 param = NDS::ARM7Read32(chan->CurNote->NoteUnk00 + 8); + + u32 soundbuf; + u32 seqdata; + int unk15; + int r8; + int pan; + bool restart = false; + if ((param & 0x7) == 7) + { + u32 idx = (param >> 3) & 0x7F; + if (idx < 0x40) + { + soundbuf = SoundBuffer[0]; + } + else + { + soundbuf = SoundBuffer[1]; + idx -= 0x40; + } + + seqdata = soundbuf + NDS::ARM7Read32(soundbuf + 0x8); + unk15 = chan->ChanUnk15 - 0x24; + + int idxoffset = (idx * 0x24) + (unk15 >> 1); + u16 val = NDS::ARM7Read16(soundbuf + 0x814 + (idxoffset*2)); + if (unk15 & 1) + idxoffset = val >> 8; + else + idxoffset = val & 0xFF; + + u32 ptr = soundbuf + NDS::ARM7Read32(seqdata + (idx*4)); + ptr += (idxoffset * 12); + seqdata = ptr; + + param = NDS::ARM7Read32(ptr + 8); + + int tmp = ((int)param << 7) >> 24; + if (tmp < 0) + { + r8 = chan->ChanUnk15; + pan = chan->CurNote->Pan + chan->CurSequence->SeqUnk13; + } + else + { + r8 = (tmp << 16) >> 16; + pan = chan->CurSequence->SeqUnk13 + (param >> 25); + } + } + else + { + u32 idx = chan->CurNote->NoteUnk07; + seqdata = chan->CurNote->NoteUnk00; + if (idx < 0x40) + { + soundbuf = SoundBuffer[0]; + } + else + { + soundbuf = SoundBuffer[1]; + } + + // param + + r8 = chan->ChanUnk15; + pan = chan->CurNote->Pan + chan->CurSequence->SeqUnk13; + } + + if (pan < 0) pan = 0; + if (pan > 127) pan = 127; + chan->CurPan = pan; + + if (chan->Status == 2) + { + chan->Status = 1; + chan->PanPhase = 5; + chan->VolPhase = 5; + chan->PanStep = 0; + chan->VolStep = 0; + } + else if (chan->Status == 4) + { + chan->Status = 3; + chan->PanPhase = 1; + chan->VolPhase = 1; + chan->PanStep = 0; + chan->VolStep = 0; + + chan->SourceData = soundbuf + NDS::ARM7Read32(seqdata); + restart = true; + } + + if (chan->ChanUnk14) + { + int tmp = (chan->CurNote->NoteUnk08 >> 2) & 0x3; + if (!tmp) + { + chan->ChanUnk14--; + if (chan->ChanUnk14 == 0) + ChannelFM(chan); + } + } + + chan->CurVolume = UpdateChannelVolume(chan, seqdata); + chan->ChanUnk0E = UpdateChannelPan(chan, seqdata, r8); + UpdateChannelRegs(chan, param, restart); +} + + +void Process(u32 param) +{ + NDS::ScheduleEvent(NDS::Event_HLE_SoundCmd, true, 262144, Process, 0); + + if (!SharedMem) return; + if (!Inited) return; + + RunCommands(); + + u32 seqstatus = SharedMem + 0x87C00; + for (int i = 0; i < NumSequences; i++) + { + Sequence* seq = &Sequences[i]; + + UpdateSequenceVolume(seq); + + if ((seq->Flags & 0xF) == 1) + { + ProcessNotes(seq); + } + + NDS::ARM7Write16(seqstatus, seq->Flags & 0xF); + NDS::ARM7Write16(seqstatus + 0x6, seq->Volume); + NDS::ARM7Write16(seqstatus + 0xA, (u16)(s16)seq->SeqUnk06); + NDS::ARM7Write16(seqstatus + 0xC, (u16)(s16)seq->SeqUnk07); + seqstatus += 0x10; + } + + for (int i = 0; i < NumChannels; i++) + { + Channel* chan = &Channels[i]; + + if (chan->Status == 0) + { + ResetChannel(chan); + } + else + { + UpdateChannel(chan); + } + } + + if (Cmd5Val == 2) + { + s16 tbl[8] = {0, 6, 12, 6, 0, -6, -12, -6}; + Stat[1] = tbl[Stat[0] & 0x7]; + Stat[0]++; + if (Stat[0] > 96) + Stat[0] = 0; + } + else if (Cmd5Val == 3) + { + Stat[1] = (Stat[0] & 0x1) ? 24 : -24; + Stat[0]++; + if (Stat[0] > 96) + Stat[0] = 0; + } + else + { + Stat[1] = 0; + } +} + + +void OnIPCRequest(u32 data) +{ + if (IPCCmd == 0 || data < 0x10000) + { + IPCCmd = data; + return; + } + + switch (IPCCmd) + { + case 1: // set work buffer + SharedMem = data; + break; + + case 2: // init + { + NDS::ARM7IOWrite16(0x04000304, 0x0001); + SPU::Write16(0x04000500, 0x807F); + SPU::Write16(0x04000508, 0); + for (u32 i = 0; i < 16; i++) + SPU::Write32(0x04000400 + (i*16), 0); + + SoundBuffer[0] = SharedMem + NDS::ARM7Read32(SharedMem + 0x14); + SoundBuffer[1] = SoundBuffer[0] + NDS::ARM7Read32(SharedMem + 0x18); + + int numnotes[16] = {14, 10, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0}; + int n = 0; + for (int i = 0; i < 16; i++) + { + Sequences[i].NoteBuffer = &NoteBuffer[n]; + Sequences[i].MaxNotes = numnotes[i]; + n += numnotes[i]; + } + + u16 capvol = NDS::ARM7Read16(SharedMem + 0x6) & 0xFF; + SetupCapture(capvol); // TODO bugged?? + + u16 chans = NDS::ARM7Read16(SharedMem + 0xE); + u32 chan_param = ((chans & 0x7) << 8) | (((chans >> 3) & 0x3) << 16); + SetupChannels(chan_param); + + Inited = true; + + SendIPCReply(0x7, 4); + SendIPCReply(0x7, 0x10001); + } + break; + + case 3: // + { + Cmd3Val = data & 0xFF; + + for (int i = 0; i < NumSequences; i++) + { + Sequence* seq = &Sequences[i]; + + if (seq->SeqUnk12 == 0) + { + if (seq->ID <= 0x64) + continue; + } + + u32 cmd3param = (seq->ID << 22) | (Cmd3Val << 16); + Cmd3(cmd3param); + } + + SendIPCReply(0x7, 1); + SendIPCReply(0x7, 0x10001); + } + break; + + case 4: // submit command buffer + { + u32* dst = CommandBuffers[CurCommandBuffer]; + for (u32 i = 0; i < 0x40; i++) + dst[i] = NDS::ARM7Read32(data+(i*4)); + + CurCommandBuffer ^= 1; + } + break; + + case 5: + Cmd5Val = data & 0xFF; + break; + + case 10: + Cmd10Val = data & 0xFF; + break; + + default: + printf("Sound_Peach: unknown IPC command %08X %08X\n", IPCCmd, data); + break; + } + + IPCCmd = 0; +} + +} +} +} diff --git a/src/HLE_Retail/Sound_Peach.h b/src/HLE_Retail/Sound_Peach.h new file mode 100644 index 00000000..7962734f --- /dev/null +++ b/src/HLE_Retail/Sound_Peach.h @@ -0,0 +1,41 @@ +/* + Copyright 2016-2022 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 SOUND_PEACH_H +#define SOUND_PEACH_H + +#include "../types.h" + +namespace HLE +{ +namespace Retail +{ +namespace Sound_Peach +{ + +void Reset(); + +void Process(u32 param); + +void OnIPCRequest(u32 data); + +} +} +} + +#endif // SOUND_PEACH_H diff --git a/src/HLE_Retail/Wifi.cpp b/src/HLE_Retail/Wifi.cpp new file mode 100644 index 00000000..f3db8d07 --- /dev/null +++ b/src/HLE_Retail/Wifi.cpp @@ -0,0 +1,453 @@ +/* + Copyright 2016-2022 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 "../NDS.h" +#include "../NDSCart.h" +#include "../HLE.h" +#include "../FIFO.h" + +#include "IPC.h" +#include "Wifi.h" + + +namespace HLE +{ +namespace Retail +{ +namespace Wifi +{ + +struct IPCReply +{ + u16 Command; + u16 Status; + int NumExtra; + u16 Extra[16]; +}; + +FIFO IPCReplyQueue; + +u32 SharedMem[2]; + +u8 MAC[6]; + +u16 BeaconInterval; +u8 BeaconFrame[1024]; + +void WifiIPCReply(u16 cmd, u16 status, int numextra=0, u16* extra=nullptr); + + +void Reset() +{ + IPCReplyQueue.Clear(); + + SharedMem[0] = 0; + SharedMem[1] = 0; + + u8* mac = SPI_Firmware::GetWifiMAC(); + memcpy(MAC, mac, 6); + + BeaconInterval = 0; + memset(BeaconFrame, 0, sizeof(BeaconFrame)); + + u16 chanmask = 0x2082; + NDS::ARM7Write16(0x027FFCFA, chanmask); +} + + +void StartHostComm() +{ + memset(BeaconFrame, 0xFF, sizeof(BeaconFrame)); + + u32 paramblock = SharedMem[1] + 0xE8; + u8* ptr = &BeaconFrame[0]; + + u32 dd_addr = NDS::ARM7Read32(paramblock + 0x00); + u16 dd_len = NDS::ARM7Read16(paramblock + 0x04); + + u32 gameid = NDS::ARM7Read32(paramblock + 0x08); + u16 streamcode = NDS::ARM7Read16(paramblock + 0x0C); + + BeaconInterval = NDS::ARM7Read16(paramblock + 0x18); + u16 channel = NDS::ARM7Read16(paramblock + 0x32); + + u8 beacontype = 0; + if (NDS::ARM7Read16(paramblock + 0x0E) & 0x1) beacontype |= (1<<0); + if (NDS::ARM7Read16(paramblock + 0x12) & 0x1) beacontype |= (1<<1); + if (NDS::ARM7Read16(paramblock + 0x14) & 0x1) beacontype |= (1<<2); + if (NDS::ARM7Read16(paramblock + 0x16) & 0x1) beacontype |= (1<<3); + + // TX header (required by comm layer) + *(u16*)ptr = 0; ptr += 2; + *(u16*)ptr = 0; ptr += 2; + *(u16*)ptr = 0; ptr += 2; + *(u16*)ptr = 0; ptr += 2; + *(u16*)ptr = 0x14; ptr += 2; // TX rate + *(u16*)ptr = 0; ptr += 2; // length, set later + + // 802.11 header + *(u16*)ptr = 0x0080; ptr += 2; // frame control + *(u16*)ptr = 0; ptr += 2; // duration + memset(ptr, 0xFF, 6); ptr += 6; // destination MAC + memcpy(ptr, MAC, 6); ptr += 6; // source MAC + memcpy(ptr, MAC, 6); ptr += 6; // BSSID + *(u16*)ptr = 0; ptr += 2; // sequence number, set later + + // beacon body + *(u64*)ptr = 0; ptr += 8; // timestamp, set later + *(u16*)ptr = BeaconInterval; ptr += 2; // beacon interval + *(u16*)ptr = 0x0021; ptr += 2; // capability + + // SSID + /* *ptr++ = 0x00; *ptr++ = 0x20; + *(u16*)ptr = NDS::ARM7Read16(paramblock + 0x8); ptr += 2; + *(u16*)ptr = NDS::ARM7Read16(paramblock + 0xA); ptr += 2; + *(u16*)ptr = NDS::ARM7Read16(paramblock + 0xC); ptr += 2; + memset(ptr, 0, 0x1A); ptr += 0x1A;*/ + + // supported rates + *ptr++ = 0x01; *ptr++ = 0x02; + *ptr++ = 0x82; + *ptr++ = 0x84; + + // channel + *ptr++ = 0x03; *ptr++ = 0x01; + *ptr++ = (channel & 0xFF); + + // TIM + *ptr++ = 0x05; *ptr++ = 0x05; + *ptr++ = 0x00; + *ptr++ = 0x02; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + + // tag DD + *ptr++ = 0xDD; *ptr++ = (0x18 + dd_len); + *(u32*)ptr = 0x00BF0900; ptr += 4; + *(u16*)ptr = 0x000A; ptr += 2; // stepping (checkme) + *(u16*)ptr = 0; ptr += 2; // sync value (fixed later) + //*(u32*)ptr = 0x00400001; ptr += 4; + *(u32*)ptr = 0x00000100; ptr += 4; + *(u32*)ptr = gameid; ptr += 4; // game ID + *(u16*)ptr = streamcode; ptr += 2; // random stream code + *ptr++ = dd_len; + *ptr++ = beacontype; + *(u16*)ptr = cmd_len; ptr += 2; // CMD frame length + *(u16*)ptr = reply_len; ptr += 2; // REPLY frame length + for (int i = 0; i < dd_len; i++) + { + *ptr++ = NDS::ARM7Read8(dd_addr + i); + } + + int len = (int)(ptr - (&BeaconFrame[0xC])); + len = (len + 3) & ~3; + + // FCS placeholder + *(u32*)&BeaconFrame[0xC+len] = 0x1D46B6B8; + len += 4; + + // frame length + *(u16*&)BeaconFrame[0xA] = len; +} + + +void WifiIPCRetry(u32 param) +{ + u16 flag = NDS::ARM7Read16(0x027FFF96); + if (flag & 0x1) + { + NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, true, 1024, WifiIPCRetry, 0); + return; + } + + IPCReply reply = IPCReplyQueue.Read(); + WifiIPCReply(reply.Command, reply.Status, reply.NumExtra, reply.Extra); +} + +void WifiIPCReply(u16 cmd, u16 status, int numextra, u16* extra) +{ + u16 flag = NDS::ARM7Read16(0x027FFF96); + if (flag & 0x1) + { + IPCReply reply; + reply.Command = cmd; + reply.Status = status; + reply.NumExtra = numextra; + if (numextra) memcpy(reply.Extra, extra, numextra*sizeof(u16)); + IPCReplyQueue.Write(reply); + NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, false, 1024, WifiIPCRetry, 0); + return; + } + + flag |= 0x1; + NDS::ARM7Write16(0x027FFF96, flag); + + u32 replybuf = NDS::ARM7Read32(SharedMem[0]+0x8); + NDS::ARM7Write16(replybuf+0x00, cmd); + NDS::ARM7Write16(replybuf+0x02, status); + + if (cmd == 0x8) + { + NDS::ARM7Write16(replybuf+0x8, extra[0]); + if (numextra == 3) + { + NDS::ARM7Write16(replybuf+0x2C, extra[1]); + NDS::ARM7Write16(replybuf+0x2E, extra[2]); + } + } + else + { + for (int i = 0; i < numextra; i++) + { + NDS::ARM7Write16(replybuf+0x8+(i*2), extra[i]); + } + } + + SendIPCReply(0xA, replybuf, 0); + + if (!IPCReplyQueue.IsEmpty()) + NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, false, 1024, WifiIPCRetry, 0); +} + +void OnIPCRequest(u32 addr) +{ + u16 cmd = NDS::ARM7Read16(addr); + + /*printf("WIFI HLE: cmd %04X\n", cmd); + for (u32 i = 0; i < 16; i++) + { + for (u32 j = 0; j < 16; j+=4) + { + u32 varp = NDS::ARM7Read32(addr+(i*16)+j); + printf("%08X ", varp); + } + printf("\n"); + }*/ + + switch (cmd) + { + case 0x0: // init + { + SharedMem[0] = NDS::ARM7Read32(addr+0x4); + SharedMem[1] = NDS::ARM7Read32(addr+0x8); + u32 respbuf = NDS::ARM7Read32(addr+0xC); + + NDS::ARM7Write32(SharedMem[0], SharedMem[1]); + NDS::ARM7Write32(SharedMem[0]+0x8, respbuf); + + // TODO init the sharedmem buffers + // TODO other shito too!! + + NDS::ARM7Write16(SharedMem[1], 2); + + WifiIPCReply(0x0, 0); + } + break; + + case 0x2: // deinit + { + u16 status = NDS::ARM7Read16(SharedMem[1]); + if (status == 2) + { + NDS::ARM7Write16(SharedMem[1], 0); + WifiIPCReply(0x2, 0); + } + else + { + WifiIPCReply(0x2, 3); + } + } + break; + + case 0x3: // enable + { + SharedMem[0] = NDS::ARM7Read32(addr+0x4); + SharedMem[1] = NDS::ARM7Read32(addr+0x8); + u32 respbuf = NDS::ARM7Read32(addr+0xC); + + NDS::ARM7Write32(SharedMem[0], SharedMem[1]); + NDS::ARM7Write32(SharedMem[0]+0x8, respbuf); + + // TODO init the sharedmem buffers + + NDS::ARM7Write16(SharedMem[1], 1); + + WifiIPCReply(0x3, 0); + } + break; + + case 0x4: // disable + { + u16 status = NDS::ARM7Read16(SharedMem[1]); + if (status == 1) + { + NDS::ARM7Write16(SharedMem[1], 0); + WifiIPCReply(0x4, 0); + } + else + { + WifiIPCReply(0x4, 3); + } + } + break; + + case 0x7: // set host param + { + // PARAM BLOCK FORMAT + // offset size desc. + // 00 4 tag DD: pointer to extra data + // 04 2 tag DD: length of extra data (tag DD length minus 0x18) + // 06 2 + // 08 6 SSID bytes 0..5 (SSID is 32 bytes, rest is all zeroes) + // 08 4 tag DD: game ID + // 0C 2 tag DD: stream code + // 0E 2 tag DD: beacon type bit0 + // 10 2 ??? + // 12 2 tag DD: beacon type bit1 + // 14 2 tag DD: beacon type bit2 + // 16 2 tag DD: beacon type bit3 + // 18 2 beacon interval + // 32 2 channel # + // 34 2 tag DD: CMD data length + // 36 2 tag DD: REPLY data length + + // for now, the param block is just copied to sharedmem + + u32 paramblock = NDS::ARM7Read32(addr+0x4); + u32 dst = SharedMem[1] + 0xE8; + for (u32 i = 0; i < 0x40; i+=4) + { + NDS::ARM7Write32(dst+i, NDS::ARM7Read32(paramblock+i)); + } + + WifiIPCReply(0x7, 0); + } + break; + + case 0x8: // start host comm + { + u16 status = NDS::ARM7Read16(SharedMem[1]); + if (status != 2) + { + u16 ext = 0; + WifiIPCReply(0x8, 3, 1, &ext); + break; + } + + StartHostComm(); + + NDS::ARM7Write16(SharedMem[1], 7); + + u16 extra[3]; + extra[0] = 0; + extra[1] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x34); + extra[2] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x36); + WifiIPCReply(0x8, 0, 3, &extra); + + NDS::ARM7Write16(SharedMem[1]+0xC2, 1); + } + break; + + case 0xA: // start host scan + { + // scan for viable host beacons + // timeout: ~20480*64 cycles (with value 0x22) + // + // COMMAND BUFFER + // offset size desc. + // 04 4 pointer to beacon data buffer + // 08 2 timeout in milliseconds + // 0A 6 desired source MAC? seen set to FFFFFFFFFFFF + // + // REPLY BUFFER + // offset size desc. + // 08 2 ??? seen set to 4 + // 0A 6 source MAC from beacon (zero if none) + // 10 ? channel + // 36 2 length of tag DD data (minus first 8 bytes) (zero if none) + // 38 X tag DD data (minus first 8 bytes) + // + // BEACON DATA BUFFER + // offset size desc. + // 00 2 buffer length in halfwords ((tag DD length - 8 + 0x41) >> 1) + // 02 2 frame control? AND 0xFF -- 0080 + // 04 6 source MAC + // 0A 2 SSID length (0 if none) + // 0C 32 SSID (if present) + // 2C 2 beacon capability field + // 2E 2 supported rate bitmask (when bit7=1) + // bit0 = 82 / 1Mbps + // bit1 = 84 / 2Mbps + // bit2 = 8B + // bit3 = 8C + // bit4 = 92 + // bit5 = 96 + // bit6 = B0 + // bit7 = C8 + // bit8 = E0 + // bit9 = EC + // bit15 = any other rate (with bit7=1) + // 30 2 supported rate bitmask + // bit0 = 02/82 / 1Mbps + // bit1 = 04/84 / 2Mbps + // bit2 = 0B/8B + // bit3 = 0C/8C + // bit4 = 12/92 + // bit5 = 16/96 + // bit6 = 30/B0 + // bit7 = 48/C8 + // bit8 = 60/E0 + // bit9 = 6C/EC + // bit15 = any other rate + // 32 2 beacon interval + // 34 2 beacon TIM period field (0 if none) + // 36 2 beacon channel field + // 38 2 beacon CF period field (0 if none) + // 3A 2 ?? + // 3C 2 length of tag DD data (minus first 8 bytes) + // 3E 2 number of bad tag DD's (when first 4 bytes are =/= 00:09:BF:00) + // 40 X tag DD data (minus first 8 bytes) + } + break; + + case 0x1E: // measure channel + { + u16 status = NDS::ARM7Read16(SharedMem[1]); + if (status != 2) + { + WifiIPCReply(0x1E, 3); + break; + } + + u16 chan = NDS::ARM7Read16(addr+0x6); + u16 extra[2] = {chan, 1}; + + WifiIPCReply(0x1E, 0, 2, extra); + } + break; + + default: + printf("WIFI HLE: unknown command %04X\n", cmd); + break; + } +} + +} +} +} diff --git a/src/HLE_Retail/Wifi.h b/src/HLE_Retail/Wifi.h new file mode 100644 index 00000000..3a8f07e5 --- /dev/null +++ b/src/HLE_Retail/Wifi.h @@ -0,0 +1,41 @@ +/* + Copyright 2016-2022 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 HLE_RETAIL_WIFI_H +#define HLE_RETAIL_WIFI_H + +#include "../types.h" + +namespace HLE +{ +namespace Retail +{ +namespace Wifi +{ + +void Reset(); + +// + +void OnIPCRequest(u32 data); + +} +} +} + +#endif // HLE_RETAIL_WIFI_H diff --git a/src/NDS.cpp b/src/NDS.cpp index e37a1944..69c62be1 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -45,6 +45,8 @@ #include "DSi_Camera.h" #include "DSi_DSP.h" +#include "HLE.h" + using Platform::Log; using Platform::LogLevel; @@ -95,7 +97,7 @@ u64 FrameStartTimestamp; int CurCPU; -const s32 kMaxIterationCycles = 64; +const s32 kMaxIterationCycles = 4096;//64; const s32 kIterationCycleMargin = 8; u32 ARM9ClockShift; @@ -683,6 +685,9 @@ void Reset() SPU::SetDegrade10Bit(degradeAudio); AREngine::Reset(); + + + HLE::Reset(); } void Start() @@ -815,10 +820,7 @@ bool DoSavestate(Savestate* file) u32 console; file->Var32(&console); if (console != ConsoleType) - { - Log(LogLevel::Error, "savestate: Expected console type %d, got console type %d. cannot load.\n", ConsoleType, console); return false; - } } file->VarArray(MainRAM, MainRAMMaxSize); @@ -873,11 +875,7 @@ bool DoSavestate(Savestate* file) file->VarArray(DMA9Fill, 4*sizeof(u32)); - if (!DoSavestate_Scheduler(file)) - { - Platform::Log(Platform::LogLevel::Error, "savestate: failed to %s scheduler state\n", file->Saving ? "save" : "load"); - return false; - } + if (!DoSavestate_Scheduler(file)) return false; file->Var32(&SchedListMask); file->Var64(&ARM9Timestamp); file->Var64(&ARM9Target); @@ -944,8 +942,6 @@ bool DoSavestate(Savestate* file) } #endif - file->Finish(); - return true; } @@ -1102,7 +1098,7 @@ u32 RunFrame() target = ARM9Timestamp >> ARM9ClockShift; CurCPU = 1; - while (ARM7Timestamp < target) + /*while (ARM7Timestamp < target) { ARM7Target = target; // might be changed by a reschedule @@ -1125,7 +1121,8 @@ u32 RunFrame() } RunTimers(1); - } + }*/ + ARM7Timestamp = target; RunSystem(target); @@ -2006,9 +2003,9 @@ void debug(u32 param) //for (int i = 0; i < 9; i++) // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); - +return; FILE* - shit = fopen("debug/crayon.bin", "wb"); + shit = fopen("debug/mkds.bin", "wb"); fwrite(ARM9->ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { @@ -2047,7 +2044,7 @@ void debug(u32 param) u8 ARM9Read8(u32 addr) -{ +{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON8 %08X %08X\n", addr, ARM9->R[15]); if ((addr & 0xFFFFF000) == 0xFFFF0000) { return *(u8*)&ARM9BIOS[addr & 0xFFF]; @@ -2105,7 +2102,7 @@ u8 ARM9Read8(u32 addr) } u16 ARM9Read16(u32 addr) -{ +{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON16 %08X %08X\n", addr, ARM9->R[15]); addr &= ~0x1; if ((addr & 0xFFFFF000) == 0xFFFF0000) @@ -2165,7 +2162,7 @@ u16 ARM9Read16(u32 addr) } u32 ARM9Read32(u32 addr) -{ +{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON32 %08X %08X\n", addr, ARM9->R[15]); addr &= ~0x3; if ((addr & 0xFFFFF000) == 0xFFFF0000) @@ -2271,7 +2268,7 @@ void ARM9Write8(u32 addr, u8 val) } void ARM9Write16(u32 addr, u16 val) -{ +{if(addr==0x027FFF96) printf("KPROAON %04X %08X\n", val, ARM9->R[15]); addr &= ~0x1; switch (addr & 0xFF000000) @@ -3457,6 +3454,7 @@ void ARM9IOWrite16(u32 addr, u16 val) { SetIRQ(1, IRQ_IPCSync); } + HLE::OnIPCSync(); return; case 0x04000184: @@ -3654,6 +3652,7 @@ void ARM9IOWrite32(u32 addr, u32 val) { bool wasempty = IPCFIFO9.IsEmpty(); IPCFIFO9.Write(val); + HLE::OnIPCRequest(); if ((IPCFIFOCnt7 & 0x0400) && wasempty) SetIRQ(1, IRQ_IPCRecv); } diff --git a/src/NDS.h b/src/NDS.h index 404e07c2..4644d764 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -52,6 +52,19 @@ enum Event_DSi_CamTransfer, Event_DSi_DSP, + // HLE + Event_HLE_PollInput, + Event_HLE_SoundCmd, + Event_HLE_SoundAlarm0, + Event_HLE_SoundAlarm1, + Event_HLE_SoundAlarm2, + Event_HLE_SoundAlarm3, + Event_HLE_SoundAlarm4, + Event_HLE_SoundAlarm5, + Event_HLE_SoundAlarm6, + Event_HLE_SoundAlarm7, + Event_HLE_WifiIPCRetry, + Event_MAX }; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 12f7ad1d..83d3bb01 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -194,14 +194,14 @@ void Key2_Encrypt(u8* data, u32 len) } -CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump) +CartCommon::CartCommon(u8* rom, u32 len, u32 chipid) { ROM = rom; ROMLength = len; ChipID = chipid; u8 unitcode = ROM[0x12]; - IsDSi = (unitcode & 0x02) != 0 && !badDSiDump; + IsDSi = (unitcode & 0x02) != 0; DSiBase = *(u16*)&ROM[0x92] << 19; } @@ -403,7 +403,7 @@ void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) } -CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump) : CartCommon(rom, len, chipid, badDSiDump) +CartRetail::CartRetail(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) { SRAM = nullptr; } @@ -865,7 +865,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) } -CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false) +CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) { } @@ -1092,7 +1092,7 @@ void CartRetailNAND::BuildSRAMID() } -CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump) : CartRetail(rom, len, chipid, badDSiDump) +CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion) : CartRetail(rom, len, chipid) { IRVersion = irversion; } @@ -1138,7 +1138,7 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last) } -CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false) +CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) { Log(LogLevel::Info,"POKETYPE CART\n"); } @@ -1172,7 +1172,7 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) } -CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid, false) +CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) { SD = nullptr; } @@ -1630,15 +1630,6 @@ bool LoadROM(const u8* romdata, u32 romlen) u8 unitcode = Header.UnitCode; bool dsi = (unitcode & 0x02) != 0; - bool badDSiDump = false; - - u32 dsiRegion = *(u32*)&CartROM[0x1B0]; - if (dsi && dsiRegion == 0) - { - Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n"); - badDSiDump = true; - dsi = false; - } size_t bannersize = dsi ? 0x23C0 : 0xA40; if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize)) @@ -1734,11 +1725,11 @@ bool LoadROM(const u8* romdata, u32 romlen) else if (CartID & 0x08000000) Cart = new CartRetailNAND(CartROM, CartROMSize, CartID); else if (irversion != 0) - Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion, badDSiDump); + Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion); else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx Cart = new CartRetailBT(CartROM, CartROMSize, CartID); else - Cart = new CartRetail(CartROM, CartROMSize, CartID, badDSiDump); + Cart = new CartRetail(CartROM, CartROMSize, CartID); if (Cart) Cart->Reset(); diff --git a/src/NDSCart.h b/src/NDSCart.h index c31852a3..27182976 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -33,7 +33,7 @@ namespace NDSCart class CartCommon { public: - CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump); + CartCommon(u8* rom, u32 len, u32 chipid); virtual ~CartCommon(); virtual u32 Type() { return 0x001; } @@ -75,7 +75,7 @@ protected: class CartRetail : public CartCommon { public: - CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump); + CartRetail(u8* rom, u32 len, u32 chipid); virtual ~CartRetail() override; virtual u32 Type() override { return 0x101; } @@ -145,7 +145,7 @@ private: class CartRetailIR : public CartRetail { public: - CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump); + CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion); ~CartRetailIR() override; virtual u32 Type() override { return 0x103; } diff --git a/src/SPI.cpp b/src/SPI.cpp index ca672347..c48ac48d 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -764,6 +764,9 @@ void Reset() ConvResult = 0; + TouchX = 0; + TouchY = 0xFFF; + MicBufferLen = 0; } diff --git a/src/Savestate.cpp b/src/Savestate.cpp index 546c16e0..0aa0ba37 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -17,16 +17,12 @@ */ #include -#include -#include #include "Savestate.h" #include "Platform.h" using Platform::Log; using Platform::LogLevel; -static const char* SAVESTATE_MAGIC = "MELN"; - /* Savestate format @@ -50,148 +46,230 @@ static const char* SAVESTATE_MAGIC = "MELN"; * different minor means adjustments may have to be made */ -Savestate::Savestate(void *buffer, u32 size, bool save) : - Error(false), - Saving(save), - CurSection(NO_SECTION), - buffer(static_cast(buffer)), - buffer_offset(0), - buffer_length(size), - buffer_owned(false), - finished(false) +// TODO: buffering system! or something of that sort +// repeated fread/fwrite is slow on Switch + +Savestate::Savestate(const std::string& filename, bool save) { - if (Saving) + const char* magic = "MELN"; + + Error = false; + + if (save) { - WriteSavestateHeader(); + Saving = true; + file = Platform::OpenLocalFile(filename, "wb"); + if (!file) + { + Log(LogLevel::Error, "savestate: file %s doesn't exist\n", filename.c_str()); + Error = true; + return; + } + + VersionMajor = SAVESTATE_MAJOR; + VersionMinor = SAVESTATE_MINOR; + + fwrite(magic, 4, 1, file); + fwrite(&VersionMajor, 2, 1, file); + fwrite(&VersionMinor, 2, 1, file); + fseek(file, 8, SEEK_CUR); // length to be fixed later } else { - // Ensure that the file starts with "MELN" - u32 read_magic = 0; - Var32(&read_magic); - - if (read_magic != *((u32*)SAVESTATE_MAGIC)) + Saving = false; + file = Platform::OpenFile(filename, "rb"); + if (!file) { - Log(LogLevel::Error, "savestate: expected magic number %#08x (%s), got %#08x\n", - *((u32*)SAVESTATE_MAGIC), - SAVESTATE_MAGIC, - read_magic - ); + Log(LogLevel::Error, "savestate: file %s doesn't exist\n", filename.c_str()); Error = true; return; } - u16 major = 0; - Var16(&major); - if (major != SAVESTATE_MAJOR) + u32 len; + fseek(file, 0, SEEK_END); + len = (u32)ftell(file); + fseek(file, 0, SEEK_SET); + + u32 buf = 0; + + fread(&buf, 4, 1, file); + if (buf != ((u32*)magic)[0]) { - Log(LogLevel::Error, "savestate: bad version major %d, expecting %d\n", major, SAVESTATE_MAJOR); + Log(LogLevel::Error, "savestate: invalid magic %08X\n", buf); Error = true; return; } - u16 minor = 0; - Var16(&minor); - if (minor > SAVESTATE_MINOR) + VersionMajor = 0; + VersionMinor = 0; + + fread(&VersionMajor, 2, 1, file); + if (VersionMajor != SAVESTATE_MAJOR) { - Log(LogLevel::Error, "savestate: state from the future, %d > %d\n", minor, SAVESTATE_MINOR); + Log(LogLevel::Error, "savestate: bad version major %d, expecting %d\n", VersionMajor, SAVESTATE_MAJOR); Error = true; return; } - u32 read_length = 0; - Var32(&read_length); - if (read_length != buffer_length) + fread(&VersionMinor, 2, 1, file); + if (VersionMinor > SAVESTATE_MINOR) { - Log(LogLevel::Error, "savestate: expected a length of %d, got %d\n", buffer_length, read_length); + Log(LogLevel::Error, "savestate: state from the future, %d > %d\n", VersionMinor, SAVESTATE_MINOR); Error = true; return; } - // The next 4 bytes are reserved - buffer_offset += 4; - } -} + buf = 0; + fread(&buf, 4, 1, file); + if (buf != len) + { + Log(LogLevel::Error, "savestate: bad length %d\n", buf); + Error = true; + return; + } - -Savestate::Savestate(u32 initial_size) : - Error(false), - Saving(true), // Can't load from an empty buffer - CurSection(NO_SECTION), - buffer(nullptr), - buffer_offset(0), - buffer_length(initial_size), - buffer_owned(true), - finished(false) -{ - buffer = static_cast(malloc(buffer_length)); - - if (buffer == nullptr) - { - Log(LogLevel::Error, "savestate: failed to allocate %d bytes\n", buffer_length); - Error = true; - return; + fseek(file, 4, SEEK_CUR); } - WriteSavestateHeader(); + CurSection = -1; } Savestate::~Savestate() { - if (Saving && !finished && !buffer_owned && !Error) - { // If we haven't finished saving, and there hasn't been an error... - Finish(); - // No need to close the active section for an owned buffer, - // it's about to be thrown out. + if (Error) return; + + if (Saving) + { + if (CurSection != 0xFFFFFFFF) + { + u32 pos = (u32)ftell(file); + fseek(file, CurSection+4, SEEK_SET); + + u32 len = pos - CurSection; + fwrite(&len, 4, 1, file); + + fseek(file, pos, SEEK_SET); + } + + fseek(file, 0, SEEK_END); + u32 len = (u32)ftell(file); + fseek(file, 8, SEEK_SET); + fwrite(&len, 4, 1, file); } - if (buffer_owned) - { - free(buffer); - } + if (file) fclose(file); } void Savestate::Section(const char* magic) { - if (Error || finished) return; + if (Error) return; if (Saving) { - // Go back to the current section's header and write the length - CloseCurrentSection(); + if (CurSection != 0xFFFFFFFF) + { + u32 pos = (u32)ftell(file); + fseek(file, CurSection+4, SEEK_SET); - CurSection = buffer_offset; + u32 len = pos - CurSection; + fwrite(&len, 4, 1, file); - // Write the new section's magic number - VarArray((void*)magic, 4); + fseek(file, pos, SEEK_SET); + } - // The next 4 bytes are the length, which we'll come back to later. - u32 zero = 0; - Var32(&zero); + CurSection = (u32)ftell(file); - // The 8 bytes afterward are reserved, so we skip them. - Var32(&zero); - Var32(&zero); + fwrite(magic, 4, 1, file); + fseek(file, 12, SEEK_CUR); } else { - u32 section_offset = FindSection(magic); + fseek(file, 0x10, SEEK_SET); - if (section_offset != NO_SECTION) + for (;;) { - buffer_offset = section_offset; - } - else - { - Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); - Error = true; + u32 buf = 0; + + fread(&buf, 4, 1, file); + if (buf != ((u32*)magic)[0]) + { + if (buf == 0) + { + Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); + return; + } + + buf = 0; + fread(&buf, 4, 1, file); + fseek(file, buf-8, SEEK_CUR); + continue; + } + + fseek(file, 12, SEEK_CUR); + break; } } } +void Savestate::Var8(u8* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 1, 1, file); + } + else + { + fread(var, 1, 1, file); + } +} + +void Savestate::Var16(u16* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 2, 1, file); + } + else + { + fread(var, 2, 1, file); + } +} + +void Savestate::Var32(u32* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 4, 1, file); + } + else + { + fread(var, 4, 1, file); + } +} + +void Savestate::Var64(u64* var) +{ + if (Error) return; + + if (Saving) + { + fwrite(var, 8, 1, file); + } + else + { + fread(var, 8, 1, file); + } +} + void Savestate::Bool32(bool* var) { - // for compatibility + // for compability if (Saving) { u32 val = *var; @@ -207,175 +285,14 @@ void Savestate::Bool32(bool* var) void Savestate::VarArray(void* data, u32 len) { - if (Error || finished) return; - - assert(buffer_offset <= buffer_length); + if (Error) return; if (Saving) { - if (buffer_offset + len > buffer_length) - { // If writing the given data would take us past the buffer's end... - Log(LogLevel::Warn, "savestate: %u-byte write would exceed %u-byte savestate buffer\n", len, buffer_length); - - if (!(buffer_owned && Resize(buffer_length * 2 + len))) - { // If we're not allowed to resize this buffer, or if we are but failed... - Log(LogLevel::Error, "savestate: Failed to write %d bytes to savestate\n", len); - Error = true; - return; - } - // The buffer's length is doubled, plus however much memory is needed for this write. - // This way we can write the data and reduce the chance of needing to resize again. - } - - memcpy(buffer + buffer_offset, data, len); + fwrite(data, len, 1, file); } else { - if (buffer_offset + len > buffer_length) - { // If reading the requested amount of data would take us past the buffer's edge... - Log(LogLevel::Error, "savestate: %u-byte read would exceed %u-byte savestate buffer\n", len, buffer_length); - Error = true; - return; - - // Can't realloc here. - // Not only do we not own the buffer pointer (when loading a state), - // but we can't magically make the desired data appear. - } - - memcpy(data, buffer + buffer_offset, len); - } - - buffer_offset += len; -} - -void Savestate::Finish() -{ - if (Error || finished) return; - CloseCurrentSection(); - WriteStateLength(); - finished = true; -} - -void Savestate::Rewind(bool save) -{ - Error = false; - Saving = save; - CurSection = NO_SECTION; - - buffer_offset = 0; - finished = false; -} - -void Savestate::CloseCurrentSection() -{ - if (CurSection != NO_SECTION && !finished) - { // If we're in the middle of writing a section... - - // Go back to the section's header - // Get the length of the section we've written thus far - u32 section_length = buffer_offset - CurSection; - - // Write the length in the section's header - // (specifically the first 4 bytes after the magic number) - memcpy(buffer + CurSection + 4, §ion_length, sizeof(section_length)); - - CurSection = NO_SECTION; + fread(data, len, 1, file); } } - -bool Savestate::Resize(u32 new_length) -{ - if (!buffer_owned) - { // If we're not allowed to resize this buffer... - Log(LogLevel::Error, "savestate: Buffer is externally-owned, cannot resize it\n"); - return false; - } - - u32 old_length = buffer_length; - void* resized = realloc(buffer, new_length); - if (!resized) - { // If the buffer couldn't be expanded... - Log(LogLevel::Error, "savestate: Failed to resize owned savestate buffer from %dB to %dB\n", old_length, new_length); - return false; - } - - u32 length_diff = new_length - old_length; - buffer = static_cast(resized); - buffer_length = new_length; - - Log(LogLevel::Debug, "savestate: Expanded %uB savestate buffer to %uB\n", old_length, new_length); - // Zero out the newly-allocated memory (to ensure we don't introduce a security hole) - memset(buffer + old_length, 0, length_diff); - return true; -} - -void Savestate::WriteSavestateHeader() -{ - // The magic number - VarArray((void *) SAVESTATE_MAGIC, 4); - - // The major and minor versions - u16 major = SAVESTATE_MAJOR; - Var16(&major); - - u16 minor = SAVESTATE_MINOR; - Var16(&minor); - - // The next 4 bytes are the file's length, which will be filled in at the end - u32 zero = 0; - Var32(&zero); - - // The following 4 bytes are reserved - Var32(&zero); -} - -void Savestate::WriteStateLength() -{ - // Not to be confused with the buffer length. - // The buffer might not be full, - // so we don't want to write out the extra stuff. - u32 state_length = buffer_offset; - - // Write the length in the header - memcpy(buffer + 0x08, &state_length, sizeof(state_length)); -} - -u32 Savestate::FindSection(const char* magic) const -{ - if (!magic) return NO_SECTION; - - // Start looking at the savestate's beginning, right after its global header - // (we can't start from the current offset because then we'd lose the ability to rearrange sections) - - for (u32 offset = 0x10; offset < buffer_length;) - { // Until we've found the desired section... - - // Get this section's magic number - char read_magic[4] = {0}; - memcpy(read_magic, buffer + offset, sizeof(read_magic)); - - if (memcmp(read_magic, magic, sizeof(read_magic)) == 0) - { // If this is the right section... - return offset + 16; // ...return the offset of the first byte of the section after the header - } - - // Haven't found our section yet. Let's move on to the next one. - - u32 section_length_offset = offset + sizeof(read_magic); - if (section_length_offset >= buffer_length) - { // If trying to read the section length would take us past the file's end... - break; - } - - // First we need to find out how big this section is... - u32 section_length = 0; - memcpy(§ion_length, buffer + section_length_offset, sizeof(section_length)); - - // ...then skip it. (The section length includes the 16-byte header.) - offset += section_length; - } - - // We've reached the end of the file without finding the requested section... - Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); - return NO_SECTION; -} \ No newline at end of file diff --git a/src/Savestate.h b/src/Savestate.h index 0aef517e..a2216ce2 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -19,7 +19,6 @@ #ifndef SAVESTATE_H #define SAVESTATE_H -#include #include #include #include "types.h" @@ -30,92 +29,37 @@ class Savestate { public: - static constexpr u32 DEFAULT_SIZE = 32 * 1024 * 1024; // 32 MB - Savestate(void* buffer, u32 size, bool save); - explicit Savestate(u32 initial_size = DEFAULT_SIZE); - + Savestate(const std::string& filename, bool save); ~Savestate(); bool Error; bool Saving; + u32 VersionMajor; + u32 VersionMinor; u32 CurSection; void Section(const char* magic); - void Var8(u8* var) - { - VarArray(var, sizeof(*var)); - } - - void Var16(u16* var) - { - VarArray(var, sizeof(*var)); - } - - void Var32(u32* var) - { - VarArray(var, sizeof(*var)); - } - - void Var64(u64* var) - { - VarArray(var, sizeof(*var)); - } + void Var8(u8* var); + void Var16(u16* var); + void Var32(u32* var); + void Var64(u64* var); void Bool32(bool* var); void VarArray(void* data, u32 len); - void Finish(); - - // TODO rewinds the stream - void Rewind(bool save); - - bool IsAtLeastVersion(u32 major, u32 minor) + bool IsAtleastVersion(u32 major, u32 minor) { - u16 major_version = MajorVersion(); - if (MajorVersion() > major) return true; - if (major_version == major && MinorVersion() >= minor) return true; + if (VersionMajor > major) return true; + if (VersionMajor == major && VersionMinor >= minor) return true; return false; } - void* Buffer() { return buffer; } - [[nodiscard]] const void* Buffer() const { return buffer; } - - [[nodiscard]] u32 BufferLength() const { return buffer_length; } - - [[nodiscard]] u32 Length() const { return buffer_offset; } - - [[nodiscard]] u16 MajorVersion() const - { - // major version is stored at offset 0x04 - u16 major = 0; - memcpy(&major, buffer + 0x04, sizeof(major)); - return major; - } - - [[nodiscard]] u16 MinorVersion() const - { - // minor version is stored at offset 0x06 - u16 minor = 0; - memcpy(&minor, buffer + 0x06, sizeof(minor)); - return minor; - } - private: - static constexpr u32 NO_SECTION = 0xffffffff; - void CloseCurrentSection(); - bool Resize(u32 new_length); - void WriteSavestateHeader(); - void WriteStateLength(); - u32 FindSection(const char* magic) const; - u8* buffer; - u32 buffer_offset; - u32 buffer_length; - bool buffer_owned; - bool finished; + FILE* file; }; #endif // SAVESTATE_H diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 80f46525..95337e11 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #ifdef ARCHIVE_SUPPORT_ENABLED @@ -53,7 +52,6 @@ std::string BaseGBAAssetName = ""; SaveManager* NDSSave = nullptr; SaveManager* GBASave = nullptr; -std::unique_ptr BackupState = nullptr; bool SavestateLoaded = false; std::string PreviousSaveFile = ""; @@ -306,62 +304,35 @@ bool SavestateExists(int slot) bool LoadState(const std::string& filename) { - FILE* file = fopen(filename.c_str(), "rb"); - if (file == nullptr) - { // If we couldn't open the state file... - Platform::Log(Platform::LogLevel::Error, "Failed to open state file \"%s\"\n", filename.c_str()); - return false; - } + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; - std::unique_ptr backup = std::make_unique(Savestate::DEFAULT_SIZE); - if (backup->Error) - { // If we couldn't allocate memory for the backup... - Platform::Log(Platform::LogLevel::Error, "Failed to allocate memory for state backup\n"); - fclose(file); - return false; - } + bool failed = false; - if (!NDS::DoSavestate(backup.get()) || backup->Error) - { // Back up the emulator's state. If that failed... - Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str()); - fclose(file); - return false; - } - // We'll store the backup once we're sure that the state was loaded. - // Now that we know the file and backup are both good, let's load the new state. - - // Get the size of the file that we opened - if (fseek(file, 0, SEEK_END) != 0) + Savestate* state = new Savestate(filename, false); + if (state->Error) { - Platform::Log(Platform::LogLevel::Error, "Failed to seek to end of state file \"%s\"\n", filename.c_str()); - fclose(file); - return false; - } - size_t size = ftell(file); - rewind(file); // reset the filebuf's position + delete state; - // Allocate exactly as much memory as we need for the savestate - std::vector buffer(size); - if (fread(buffer.data(), size, 1, file) == 0) - { // Read the state file into the buffer. If that failed... - Platform::Log(Platform::LogLevel::Error, "Failed to read %u-byte state file \"%s\"\n", size, filename.c_str()); - fclose(file); - return false; - } - fclose(file); // done with the file now - - // Get ready to load the state from the buffer into the emulator - std::unique_ptr state = std::make_unique(buffer.data(), size, false); - - if (!NDS::DoSavestate(state.get()) || state->Error) - { // If we couldn't load the savestate from the buffer... - Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str()); - return false; + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; } - // The backup was made and the state was loaded, so we can store the backup now. - BackupState = std::move(backup); // This will clean up any existing backup - assert(backup == nullptr); + bool res = NDS::DoSavestate(state); + delete state; + + if (!res) + { + failed = true; + state = new Savestate("timewarp.mln", false); + NDS::DoSavestate(state); + delete state; + } + + if (failed) return false; if (Config::SavestateRelocSRAM && NDSSave) { @@ -380,41 +351,15 @@ bool LoadState(const std::string& filename) bool SaveState(const std::string& filename) { - FILE* file = fopen(filename.c_str(), "wb"); - - if (file == nullptr) - { // If the file couldn't be opened... - return false; - } - - Savestate state; - if (state.Error) - { // If there was an error creating the state (and allocating its memory)... - fclose(file); - return false; - } - - // Write the savestate to the in-memory buffer - NDS::DoSavestate(&state); - - if (state.Error) + Savestate* state = new Savestate(filename, true); + if (state->Error) { - fclose(file); + delete state; return false; } - if (fwrite(state.Buffer(), state.Length(), 1, file) == 0) - { // Write the Savestate buffer to the file. If that fails... - Platform::Log(Platform::Error, - "Failed to write %d-byte savestate to %s\n", - state.Length(), - filename.c_str() - ); - fclose(file); - return false; - } - - fclose(file); + NDS::DoSavestate(state); + delete state; if (Config::SavestateRelocSRAM && NDSSave) { @@ -429,14 +374,14 @@ bool SaveState(const std::string& filename) void UndoStateLoad() { - if (!SavestateLoaded || !BackupState) return; + if (!SavestateLoaded) return; - // Rewind the backup state and put it in load mode - BackupState->Rewind(false); // pray that this works // what do we do if it doesn't??? // but it should work. - NDS::DoSavestate(BackupState.get()); + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; if (NDSSave && (!PreviousSaveFile.empty())) { @@ -569,14 +514,6 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) return realSize; } -void ClearBackupState() -{ - if (BackupState != nullptr) - { - BackupState = nullptr; - } -} - bool LoadROM(QStringList filepath, bool reset) { if (filepath.empty()) return false; diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 1ec0fe51..efaed36b 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -35,7 +35,6 @@ extern SaveManager* GBASave; QString VerifySetup(); void Reset(); bool LoadBIOS(); -void ClearBackupState(); bool LoadROM(QStringList filepath, bool reset); void EjectCart(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 4ed7d3fc..5a12b5cc 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1057,6 +1057,8 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256 * 192 * 4); emuThread->FrontBufferLock.unlock(); + painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter != 0); + QRect screenrc(0, 0, 256, 192); for (int i = 0; i < numScreens; i++) @@ -1873,8 +1875,6 @@ void MainWindow::createScreenPanel() } setCentralWidget(panelWidget); - actScreenFiltering->setEnabled(hasOGL); - connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); emit screenLayoutChange(); } @@ -1923,7 +1923,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) if (event->isAutoRepeat()) return; // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) NDS::debug(0); + if (event->key() == Qt::Key_F11) NDS::debug(0); Input::KeyPress(event); }