From a92727e8623af9d5ee3959729cba677dad514ea9 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 1 Apr 2014 21:55:19 +0200 Subject: [PATCH 01/59] DSPHLE: Add a config parameter to dump UCode to disk --- Source/Core/Core/ConfigManager.cpp | 2 ++ Source/Core/Core/ConfigManager.h | 1 + Source/Core/Core/HW/DSPHLE/UCodes/ROM.cpp | 19 ++++++++++--------- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 20 +++++++++++--------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 3f1ba69e06..68aaada219 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -284,6 +284,7 @@ void SConfig::SaveDSPSettings(IniFile& ini) dsp->Set("EnableJIT", m_DSPEnableJIT); dsp->Set("DumpAudio", m_DumpAudio); + dsp->Set("DumpUCode", m_DumpUCode); dsp->Set("Backend", sBackend); dsp->Set("Volume", m_Volume); dsp->Set("CaptureLog", m_DSPCaptureLog); @@ -543,6 +544,7 @@ void SConfig::LoadDSPSettings(IniFile& ini) dsp->Get("EnableJIT", &m_DSPEnableJIT, true); dsp->Get("DumpAudio", &m_DumpAudio, false); + dsp->Get("DumpUCode", &m_DumpUCode, false); #if defined __linux__ && HAVE_ALSA dsp->Get("Backend", &sBackend, BACKEND_ALSA); #elif defined __APPLE__ diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index b37b742e5e..caeec25152 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -255,6 +255,7 @@ struct SConfig : NonCopyable bool m_DSPCaptureLog; bool m_DumpAudio; bool m_IsMuted; + bool m_DumpUCode; int m_Volume; std::string sBackend; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/ROM.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/ROM.cpp index 6492fd2d1d..0d54b6efa0 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/ROM.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/ROM.cpp @@ -92,17 +92,18 @@ void ROMUCode::BootUCode() (u8*)HLEMemory_Get_Pointer(m_current_ucode.m_ram_address), m_current_ucode.m_length); -#if defined(_DEBUG) || defined(DEBUGFAST) - std::string ucode_dump_path = StringFromFormat( - "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX).c_str(), ector_crc); - - File::IOFile fp(ucode_dump_path, "wb"); - if (fp) + if (SConfig::GetInstance().m_DumpUCode) { - fp.WriteArray((u8*)HLEMemory_Get_Pointer(m_current_ucode.m_ram_address), - m_current_ucode.m_length); + std::string ucode_dump_path = StringFromFormat( + "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX).c_str(), ector_crc); + + File::IOFile fp(ucode_dump_path, "wb"); + if (fp) + { + fp.WriteArray((u8*)HLEMemory_Get_Pointer(m_current_ucode.m_ram_address), + m_current_ucode.m_length); + } } -#endif DEBUG_LOG(DSPHLE, "CurrentUCode SOURCE Addr: 0x%08x", m_current_ucode.m_ram_address); DEBUG_LOG(DSPHLE, "CurrentUCode Length: 0x%08x", m_current_ucode.m_length); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index f3c17a69e2..24e3732b61 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -9,6 +9,7 @@ #include "Common/Hash.h" #include "Common/StringUtil.h" +#include "Core/ConfigManager.h" #include "Core/HW/DSPHLE/UCodes/AX.h" #include "Core/HW/DSPHLE/UCodes/AXWii.h" #include "Core/HW/DSPHLE/UCodes/CARD.h" @@ -142,17 +143,18 @@ void UCodeInterface::PrepareBootUCode(u32 mail) (u8*)HLEMemory_Get_Pointer(m_next_ucode.iram_mram_addr), m_next_ucode.iram_size); -#if defined(_DEBUG) || defined(DEBUGFAST) - std::string ucode_dump_path = StringFromFormat( - "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX).c_str(), ector_crc); - - File::IOFile fp(ucode_dump_path, "wb"); - if (fp) + if (SConfig::GetInstance().m_DumpUCode) { - fp.WriteArray((u8*)Memory::GetPointer(m_next_ucode.iram_mram_addr), - m_next_ucode.iram_size); + std::string ucode_dump_path = StringFromFormat( + "%sDSP_UC_%08X.bin", File::GetUserPath(D_DUMPDSP_IDX).c_str(), ector_crc); + + File::IOFile fp(ucode_dump_path, "wb"); + if (fp) + { + fp.WriteArray((u8*)Memory::GetPointer(m_next_ucode.iram_mram_addr), + m_next_ucode.iram_size); + } } -#endif DEBUG_LOG(DSPHLE, "PrepareBootUCode 0x%08x", ector_crc); DEBUG_LOG(DSPHLE, "DRAM -> MRAM: src %04x dst %08x size %04x", From 22ec2581944ba4076d4b1f85c0a7798cd25b732d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 31 Mar 2014 02:54:15 +0200 Subject: [PATCH 02/59] ZeldaHLE: Rip out the whole voice processing code. Still boots games, but no sound at all. --- Source/Core/Core/CMakeLists.txt | 3 - Source/Core/Core/Core.vcxproj | 3 - Source/Core/Core/Core.vcxproj.filters | 9 - Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 120 +-- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 187 ----- .../Core/Core/HW/DSPHLE/UCodes/ZeldaADPCM.cpp | 68 -- .../Core/Core/HW/DSPHLE/UCodes/ZeldaSynth.cpp | 173 ---- .../Core/Core/HW/DSPHLE/UCodes/ZeldaVoice.cpp | 790 ------------------ 8 files changed, 7 insertions(+), 1346 deletions(-) delete mode 100644 Source/Core/Core/HW/DSPHLE/UCodes/ZeldaADPCM.cpp delete mode 100644 Source/Core/Core/HW/DSPHLE/UCodes/ZeldaSynth.cpp delete mode 100644 Source/Core/Core/HW/DSPHLE/UCodes/ZeldaVoice.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 636d8fa928..207743bbd0 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -74,9 +74,6 @@ set(SRCS ActionReplay.cpp HW/DSPHLE/UCodes/ROM.cpp HW/DSPHLE/UCodes/UCodes.cpp HW/DSPHLE/UCodes/Zelda.cpp - HW/DSPHLE/UCodes/ZeldaADPCM.cpp - HW/DSPHLE/UCodes/ZeldaSynth.cpp - HW/DSPHLE/UCodes/ZeldaVoice.cpp HW/DSPHLE/MailHandler.cpp HW/DSPHLE/DSPHLE.cpp HW/DSPLLE/DSPDebugInterface.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index eaba8a5f96..335d505c62 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -108,9 +108,6 @@ - - - diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index b35c0caa9c..92d88bc479 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -336,15 +336,6 @@ HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - - HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - - - HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - - - HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes - HW %28Flipper/Hollywood%29\DSP Interface + HLE\HLE\uCodes diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index c18b8a6ce9..72a8b0d42e 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -23,21 +23,10 @@ ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) m_current_voice(0), m_current_buffer(0), m_num_buffers(0), - m_voice_pbs_addr(0), - m_unk_table_addr(0), - m_reverb_pbs_addr(0), - m_right_buffers_addr(0), - m_left_buffers_addr(0), - m_pos(0), - m_dma_base_addr(0), m_num_steps(0), m_list_in_progress(false), m_step(0), - m_read_offset(0), - m_mail_state(WaitForMail), - m_num_pbs(0), - m_pb_address(0), - m_pb_address2(0) + m_read_offset(0) { DEBUG_LOG(DSPHLE, "UCode_Zelda - add boot mails for handshake"); @@ -52,34 +41,11 @@ ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); m_mail_handler.PushMail(0xF3551111); // handshake } - - m_voice_buffer = new s32[256 * 1024]; - m_resample_buffer = new s16[256 * 1024]; - m_left_buffer = new s32[256 * 1024]; - m_right_buffer = new s32[256 * 1024]; - - memset(m_buffer, 0, sizeof(m_buffer)); - memset(m_sync_flags, 0, sizeof(m_sync_flags)); - memset(m_afc_coef_table, 0, sizeof(m_afc_coef_table)); - memset(m_pb_mask, 0, sizeof(m_pb_mask)); } ZeldaUCode::~ZeldaUCode() { m_mail_handler.Clear(); - - delete [] m_voice_buffer; - delete [] m_resample_buffer; - delete [] m_left_buffer; - delete [] m_right_buffer; -} - -u8 *ZeldaUCode::GetARAMPointer(u32 address) -{ - if (IsDMAVersion()) - return Memory::GetPointer(m_dma_base_addr) + address; - else - return DSP::GetARAMPtr() + address; } void ZeldaUCode::Update() @@ -116,7 +82,7 @@ void ZeldaUCode::HandleMail_LightVersion(u32 mail) { DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - MixAudio(); + // TODO(delroth): Mix audio. m_current_buffer++; @@ -176,7 +142,7 @@ void ZeldaUCode::HandleMail_SMSVersion(u32 mail) m_num_sync_mail = 0; m_sync_in_progress = false; - MixAudio(); + // TODO(delroth): Mix audio. m_current_buffer++; @@ -281,7 +247,7 @@ void ZeldaUCode::HandleMail_NormalVersion(u32 mail) if (m_current_voice >= m_num_voices) { - MixAudio(); + // TODO(delroth): Mix audio. m_current_buffer++; @@ -403,63 +369,16 @@ void ZeldaUCode::ExecuteList() // DsetupTable ... zelda ww jumps to 0x0095 case 0x01: - { - m_num_voices = extra_data; - m_voice_pbs_addr = Read32() & 0x7FFFFFFF; - m_unk_table_addr = Read32() & 0x7FFFFFFF; - m_afc_coef_table_addr = Read32() & 0x7FFFFFFF; - m_reverb_pbs_addr = Read32() & 0x7FFFFFFF; // WARNING: reverb PBs are very different from voice PBs! - - // Read the other table - u16 *tmp_ptr = (u16*)Memory::GetPointer(m_unk_table_addr); - for (int i = 0; i < 0x280; i++) - m_misc_table[i] = (s16)Common::swap16(tmp_ptr[i]); - - // Read AFC coef table - tmp_ptr = (u16*)Memory::GetPointer(m_afc_coef_table_addr); - for (int i = 0; i < 32; i++) - m_afc_coef_table[i] = (s16)Common::swap16(tmp_ptr[i]); - - DEBUG_LOG(DSPHLE, "DsetupTable"); - DEBUG_LOG(DSPHLE, "Num voice param blocks: %i", m_num_voices); - DEBUG_LOG(DSPHLE, "Voice param blocks address: 0x%08x", m_voice_pbs_addr); - - // This points to some strange data table. Don't know yet what it's for. Reverb coefs? - DEBUG_LOG(DSPHLE, "DSPRES_FILTER (size: 0x40): 0x%08x", m_unk_table_addr); - - // Zelda WW: This points to a 64-byte array of coefficients, which are EXACTLY the same - // as the AFC ADPCM coef array in decode.c of the in_cube winamp plugin, - // which can play Zelda audio. So, these should definitely be used when decoding AFC. - DEBUG_LOG(DSPHLE, "DSPADPCM_FILTER (size: 0x500): 0x%08x", m_afc_coef_table_addr); - DEBUG_LOG(DSPHLE, "Reverb param blocks address: 0x%08x", m_reverb_pbs_addr); - } + Read32(); Read32(); Read32(); Read32(); break; // SyncFrame ... zelda ww jumps to 0x0243 case 0x02: - { - m_sync_cmd_pending = true; - - m_current_buffer = 0; - m_num_buffers = (cmd_mail >> 16) & 0xFF; - - // Addresses for right & left buffers in main memory - // Each buffer is 160 bytes long. The number of (both left & right) buffers - // is set by the first mail of the list. - m_left_buffers_addr = Read32() & 0x7FFFFFFF; - m_right_buffers_addr = Read32() & 0x7FFFFFFF; - - DEBUG_LOG(DSPHLE, "DsyncFrame"); - // These alternate between three sets of mixing buffers. They are all three fairly near, - // but not at, the ADMA read addresses. - DEBUG_LOG(DSPHLE, "Right buffer address: 0x%08x", m_right_buffers_addr); - DEBUG_LOG(DSPHLE, "Left buffer address: 0x%08x", m_left_buffers_addr); - + Read32(); Read32(); if (IsLightVersion()) break; else return; - } // Simply sends the sync messages @@ -485,11 +404,7 @@ void ZeldaUCode::ExecuteList() // This opcode, in the SMG ucode, sets the base address for audio data transfers from main memory (using DMA). // In the Zelda ucode, it is dummy, because this ucode uses accelerator for audio data transfers. case 0x0e: - { - m_dma_base_addr = Read32() & 0x7FFFFFFF; - DEBUG_LOG(DSPHLE, "DsetDMABaseAddr"); - DEBUG_LOG(DSPHLE, "DMA base address: 0x%08x", m_dma_base_addr); - } + Read32(); break; // default ... zelda ww jumps to 0x0043 @@ -521,9 +436,6 @@ u32 ZeldaUCode::GetUpdateMs() void ZeldaUCode::DoState(PointerWrap &p) { - p.Do(m_afc_coef_table); - p.Do(m_misc_table); - p.Do(m_sync_in_progress); p.Do(m_max_voice); p.Do(m_sync_flags); @@ -537,17 +449,6 @@ void ZeldaUCode::DoState(PointerWrap &p) p.Do(m_current_buffer); p.Do(m_num_buffers); - p.Do(m_voice_pbs_addr); - p.Do(m_unk_table_addr); - p.Do(m_afc_coef_table_addr); - p.Do(m_reverb_pbs_addr); - - p.Do(m_right_buffers_addr); - p.Do(m_left_buffers_addr); - p.Do(m_pos); - - p.Do(m_dma_base_addr); - p.Do(m_num_steps); p.Do(m_list_in_progress); p.Do(m_step); @@ -555,12 +456,5 @@ void ZeldaUCode::DoState(PointerWrap &p) p.Do(m_read_offset); - p.Do(m_mail_state); - p.Do(m_pb_mask); - - p.Do(m_num_pbs); - p.Do(m_pb_address); - p.Do(m_pb_address2); - DoStateShared(p); } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 6785f52b70..5f65008c35 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -7,113 +7,6 @@ #include "Common/CommonTypes.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" -// Obviously missing things that must be in here, somewhere among the "unknown": -// * Volume -// * L/R Pan -// * (probably) choice of resampling algorithm (point, linear, cubic) - -union ZeldaVoicePB -{ - struct - { - // Read-Write part - u16 Status; // 0x00 | 1 = play, 0 = stop - u16 KeyOff; // 0x01 | writing 1 stops voice? - u16 RatioInt; // 0x02 | Position delta (playback speed) - u16 Unk03; // 0x03 | unknown - u16 NeedsReset; // 0x04 | indicates if some values in PB need to be reset - u16 ReachedEnd; // 0x05 | set to 1 when end reached - u16 IsBlank; // 0x06 | 0 = normal sound, 1 = samples are always the same - u16 Unk07; // 0x07 | unknown, in zelda always 0x0010. Something to do with number of saved samples (0x68)? - - u16 SoundType; // 0x08 | "Sound type": so far in zww: 0x0d00 for music (volume mode 0), 0x4861 for sfx (volume mode 1) - u16 volumeLeft1; // 0x09 | Left Volume 1 // There's probably two of each because they should be ramped within each frame. - u16 volumeLeft2; // 0x0A | Left Volume 2 - u16 Unk0B; // 0x0B | unknown - - u16 SoundType2; // 0x0C | "Sound type" 2 (not really sound type) - u16 volumeRight1; // 0x0D | Right Volume 1 - u16 volumeRight2; // 0x0E | Right Volume 2 - u16 Unk0F; // 0x0F | unknown - - u16 SoundType3; // 0x10 | "Sound type" 3 (not really sound type) - u16 volumeUnknown1_1; // 0x11 | Unknown Volume 1 - u16 volumeUnknown1_2; // 0x12 | Unknown Volume 1 - u16 Unk13; // 0x13 | unknown - - u16 SoundType4; // 0x14 | "Sound type" 4 (not really sound type) - u16 volumeUnknown2_1; // 0x15 | Unknown Volume 2 - u16 volumeUnknown2_2; // 0x16 | Unknown Volume 2 - u16 Unk17; // 0x17 | unknown - - u16 Unk18[0x10]; // 0x18 | unknown - u16 Unk28; // 0x28 | unknown - u16 Unk29; // 0x29 | unknown // multiplied by 0x2a @ 0d21/ZWW - u16 Unk2a; // 0x2A | unknown // loaded at 0d2e/ZWW - u16 Unk2b; // 0x2B | unknown - u16 VolumeMode; // 0x2C | unknown // See 0337/ZWW - u16 Unk2D; // 0x2D | unknown - u16 Unk2E; // 0x2E | unknown - u16 Unk2F; // 0x2F | unknown - u16 CurSampleFrac; // 0x30 | Fractional part of the current sample position - u16 Unk31; // 0x31 | unknown / unused - u16 CurBlock; // 0x32 | current block? used by zelda's AFC decoder. we don't need it. - u16 FixedSample; // 0x33 | sample value for "blank" voices - u32 RestartPos; // 0x34 | restart pos / "loop start offset" - u16 Unk36[2]; // 0x36 | unknown // loaded at 0adc/ZWW in 0x21 decoder - u32 CurAddr; // 0x38 | current address - u32 RemLength; // 0x3A | remaining length - u16 ResamplerOldData[4]; // 0x3C | The resampler stores the last 4 decoded samples here from the previous frame, so that the filter kernel has something to read before the start of the buffer. - u16 Unk40[0x10]; // 0x40 | Used as some sort of buffer by IIR - u16 Unk50[0x8]; // 0x50 | Used as some sort of buffer by 06ff/ZWW - u16 Unk58[0x8]; // 0x58 | - u16 Unk60[0x6]; // 0x60 | - u16 YN2; // 0x66 | YN2 - u16 YN1; // 0x67 | YN1 - u16 Unk68[0x10]; // 0x68 | Saved samples from last decode? - u16 FilterState1; // 0x78 | unknown // ZWW: 0c84_FilterBufferInPlace loads and stores. Simply, the filter state. - u16 FilterState2; // 0x79 | unknown // ZWW: same as above. these two are active if 0x04a8 != 0. - u16 Unk7A; // 0x7A | unknown - u16 Unk7B; // 0x7B | unknown - u16 Unk7C; // 0x7C | unknown - u16 Unk7D; // 0x7D | unknown - u16 Unk7E; // 0x7E | unknown - u16 Unk7F; // 0x7F | unknown - - // Read-only part - u16 Format; // 0x80 | audio format - u16 RepeatMode; // 0x81 | 0 = one-shot, non zero = loop - u16 LoopYN1; // 0x82 | YN1 reload (when AFC loops) - u16 LoopYN2; // 0x83 | YN2 reload (when AFC loops) - u16 Unk84; // 0x84 | IIR Filter # coefs? - u16 StopOnSilence; // 0x85 | Stop on silence? (Flag for something volume related. Decides the weird stuff at 035a/ZWW, alco 0cd3) - u16 Unk86; // 0x86 | unknown - u16 Unk87; // 0x87 | unknown - u32 LoopStartPos; // 0x88 | loopstart pos - u32 Length; // 0x8A | sound length - u32 StartAddr; // 0x8C | sound start address - u32 UnkAddr; // 0x8E | ??? - u16 Padding[0x10]; // 0x90 | padding - u16 Padding2[0x8]; // 0xa0 | FIR filter coefs of some sort (0xa4 controls the appearance of 0xa5-0xa7 and is almost always 0x7FFF) - u16 FilterEnable; // 0xa8 | FilterBufferInPlace enable - u16 Padding3[0x7]; // 0xa9 | padding - u16 Padding4[0x10]; // 0xb0 | padding - }; - u16 raw[0xc0]; // WARNING-do not use on parts of the 32-bit values - they are swapped! -}; - -union ZeldaUnkPB -{ - struct - { - u16 Control; // 0x00 | control - u16 Unk01; // 0x01 | unknown - u32 SrcAddr; // 0x02 | some address - u16 Unk04[0xC]; // 0x04 | unknown - }; - u16 raw[16]; -}; - class ZeldaUCode : public UCodeInterface { public: @@ -127,17 +20,8 @@ public: void HandleMail_NormalVersion(u32 mail); void Update() override; - void CopyPBsFromRAM(); - void CopyPBsToRAM(); - void DoState(PointerWrap &p) override; - int *templbuffer; - int *temprbuffer; - - // Simple dump ... - int DumpAFC(u8* pIn, const int size, const int srate); - u32 Read32() { u32 res = *(u32*)&m_buffer[m_read_offset]; @@ -197,17 +81,6 @@ private: } } - // These are the only dynamically allocated things allowed in the ucode. - s32* m_voice_buffer; - s16* m_resample_buffer; - s32* m_left_buffer; - s32* m_right_buffer; - - // If you add variables, remember to keep DoState() and the constructor up to date. - - s16 m_afc_coef_table[32]; - s16 m_misc_table[0x280]; - bool m_sync_in_progress; u32 m_max_voice; u32 m_sync_flags[16]; @@ -222,21 +95,6 @@ private: u32 m_current_buffer; u32 m_num_buffers; - // Those are set by command 0x1 (DsetupTable) - u32 m_voice_pbs_addr; - u32 m_unk_table_addr; - u32 m_afc_coef_table_addr; - u32 m_reverb_pbs_addr; - - u32 m_right_buffers_addr; - u32 m_left_buffers_addr; - //u32 m_unkAddr; - u32 m_pos; - - // Only in SMG ucode - // Set by command 0xE (DsetDMABaseAddr) - u32 m_dma_base_addr; - // List, buffer management ===================== u32 m_num_steps; bool m_list_in_progress; @@ -245,50 +103,5 @@ private: u32 m_read_offset; - enum EMailState - { - WaitForMail, - ReadingFrameSync, - ReadingMessage, - ReadingSystemMsg - }; - - EMailState m_mail_state; - u16 m_pb_mask[0x10]; - - u32 m_num_pbs; - u32 m_pb_address; // The main param block array - u32 m_pb_address2; // 4 smaller param blocks - void ExecuteList(); - - u8 *GetARAMPointer(u32 address); - - // AFC decoder - static void AFCdecodebuffer(const s16 *coef, const char *input, signed short *out, short *histp, short *hist2p, int type); - - void ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB); - void WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB); - - // Voice formats - void RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size); - void RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size); - void RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size); - void RenderSynth_WaveTable(ZeldaVoicePB &PB, s32* _Buffer, int _Size); - - void RenderVoice_PCM8(ZeldaVoicePB& PB, s16* _Buffer, int _Size); - void RenderVoice_PCM16(ZeldaVoicePB& PB, s16* _Buffer, int _Size); - - void RenderVoice_AFC(ZeldaVoicePB& PB, s16* _Buffer, int _Size); - void RenderVoice_Raw(ZeldaVoicePB& PB, s16* _Buffer, int _Size); - - void Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample = false); - - int ConvertRatio(int pb_ratio); - int SizeForResampling(ZeldaVoicePB &PB, int size); - - // Renders a voice and mixes it into LeftBuffer, RightBuffer - void RenderAddVoice(ZeldaVoicePB& PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size); - - void MixAudio(); }; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaADPCM.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaADPCM.cpp deleted file mode 100644 index 52e7ae44bb..0000000000 --- a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaADPCM.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2009 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "Common/CommonTypes.h" -#include "Common/MathUtil.h" -#include "Core/HW/DSPHLE/UCodes/Zelda.h" - -void ZeldaUCode::AFCdecodebuffer(const s16 *coef, const char *src, signed short *out, short *histp, short *hist2p, int type) -{ - // First 2 nibbles are ADPCM scale etc. - short delta = 1 << (((*src) >> 4) & 0xf); - short idx = (*src) & 0xf; - src++; - - short nibbles[16]; - if (type == 9) - { - for (int i = 0; i < 16; i += 2) - { - nibbles[i + 0] = *src >> 4; - nibbles[i + 1] = *src & 15; - src++; - } - - for (auto& nibble : nibbles) - { - if (nibble >= 8) - nibble = nibble - 16; - nibble <<= 11; - } - } - else - { - // In Pikmin, Dolphin's engine sound is using AFC type 5, even though such a sound is hard - // to compare, it seems like to sound exactly like a real GC - // In Super Mario Sunshine, you can get such a sound by talking to/jumping on anyone - for (int i = 0; i < 16; i += 4) - { - nibbles[i + 0] = (*src >> 6) & 0x03; - nibbles[i + 1] = (*src >> 4) & 0x03; - nibbles[i + 2] = (*src >> 2) & 0x03; - nibbles[i + 3] = (*src >> 0) & 0x03; - src++; - } - - for (auto& nibble : nibbles) - { - if (nibble >= 2) - nibble = nibble - 4; - nibble <<= 13; - } - } - - short hist = *histp; - short hist2 = *hist2p; - for (int i = 0; i < 16; i++) - { - int sample = delta * nibbles[i] + ((int)hist * coef[idx * 2]) + ((int)hist2 * coef[idx * 2 + 1]); - sample >>= 11; - MathUtil::Clamp(&sample, -32768, 32767); - out[i] = sample; - hist2 = hist; - hist = (short)sample; - } - *histp = hist; - *hist2p = hist2; -} diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaSynth.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaSynth.cpp deleted file mode 100644 index 054efeaddd..0000000000 --- a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaSynth.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#include "Core/HW/DSPHLE/UCodes/UCodes.h" -#include "Core/HW/DSPHLE/UCodes/Zelda.h" - -void ZeldaUCode::RenderSynth_RectWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size) -{ - s64 ratio = ((s64)PB.RatioInt << 16) * 16; - s64 TrueSamplePosition = PB.CurSampleFrac; - - // PB.Format == 0x3 -> Rectangular Wave, 0x0 -> Square Wave - unsigned int mask = PB.Format ? 3 : 1; - // int shift = PB.Format ? 2 : 1; // Unused? - - u32 pos[2] = {0, 0}; - int i = 0; - - if (PB.KeyOff != 0) - return; - - if (PB.NeedsReset) - { - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); - PB.ReachedEnd = 0; - } - -_lRestart: - if (PB.ReachedEnd) - { - PB.ReachedEnd = 0; - - if (PB.RepeatMode == 0) - { - PB.KeyOff = 1; - PB.RemLength = 0; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1) + PB.Length; - return; - } - else - { - PB.RestartPos = PB.LoopStartPos; - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); - pos[1] = 0; pos[0] = 0; - } - } - - while (i < _Size) - { - s16 sample = ((pos[1] & mask) == mask) ? 0xc000 : 0x4000; - - TrueSamplePosition += (ratio >> 16); - - _Buffer[i++] = (s32)sample; - - (*(u64*)&pos) += ratio; - if ((pos[1] + ((PB.CurAddr - PB.StartAddr) >> 1)) >= PB.Length) - { - PB.ReachedEnd = 1; - goto _lRestart; - } - } - - if (PB.RemLength < pos[1]) - { - PB.RemLength = 0; - PB.ReachedEnd = 1; - } - else - { - PB.RemLength -= pos[1]; - } - - PB.CurSampleFrac = TrueSamplePosition & 0xFFFF; -} - -void ZeldaUCode::RenderSynth_SawWave(ZeldaVoicePB &PB, s32* _Buffer, int _Size) -{ - s32 ratio = (s32)ceil((float)PB.RatioInt / 3); - s64 pos = PB.CurSampleFrac; - - for (int i = 0; i < _Size; i++) - { - pos += ratio; - _Buffer[i] = pos & 0xFFFF; - } - - PB.CurSampleFrac = pos & 0xFFFF; -} - -void ZeldaUCode::RenderSynth_Constant(ZeldaVoicePB &PB, s32* _Buffer, int _Size) -{ - // TODO: Header, footer - for (int i = 0; i < _Size; i++) - _Buffer[i] = (s32)PB.RatioInt; -} - -// A piece of code from LLE so we can see how the wrap register affects the sound - -inline u16 AddValueToReg(u32 ar, s32 ix) -{ - u32 wr = 0x3f; - u32 mx = (wr | 1) << 1; - u32 nar = ar + ix; - u32 dar = (nar ^ ar ^ ix) & mx; - - if (ix >= 0) - { - if (dar > wr) //overflow - nar -= wr + 1; - } - else - { - if ((((nar + wr + 1) ^ nar) & dar) <= wr) //underflow or below min for mask - nar += wr + 1; - } - return nar; -} - -void ZeldaUCode::RenderSynth_WaveTable(ZeldaVoicePB &PB, s32* _Buffer, int _Size) -{ - u16 address; - - switch (PB.Format) - { - default: - case 0x0004: - address = 0x140; - break; - - case 0x0007: - address = 0x100; - break; - - case 0x000b: - address = 0x180; - break; - - case 0x000c: - address = 0x1c0; - break; - } - - // TODO: Resample this! - INFO_LOG(DSPHLE, "Synthesizing the incomplete format 0x%04x", PB.Format); - - u64 ACC0 = PB.CurSampleFrac << 6; - - ACC0 &= 0xffff003fffffULL; - - address = AddValueToReg(address, ((ACC0 >> 16) & 0xffff)); - ACC0 &= 0xffff0000ffffULL; - - for (int i = 0; i < 0x50; i++) - { - _Buffer[i] = m_misc_table[address]; - - ACC0 += PB.RatioInt << 5; - address = AddValueToReg(address, ((ACC0 >> 16) & 0xffff)); - - ACC0 &= 0xffff0000ffffULL; - } - - ACC0 += address << 16; - PB.CurSampleFrac = (ACC0 >> 6) & 0xffff; -} - - diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaVoice.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaVoice.cpp deleted file mode 100644 index e6db1b0d85..0000000000 --- a/Source/Core/Core/HW/DSPHLE/UCodes/ZeldaVoice.cpp +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2009 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#include "Common/CommonFuncs.h" -#include "Common/MathUtil.h" - -#include "Core/HW/DSP.h" -#include "Core/HW/Memmap.h" -#include "Core/HW/DSPHLE/UCodes/UCodes.h" -#include "Core/HW/DSPHLE/UCodes/Zelda.h" - -void ZeldaUCode::ReadVoicePB(u32 _Addr, ZeldaVoicePB& PB) -{ - u16 *memory = (u16*)Memory::GetPointer(_Addr); - - // Perform byteswap - for (int i = 0; i < (0x180 / 2); i++) - ((u16*)&PB)[i] = Common::swap16(memory[i]); - - // Word swap all 32-bit variables. - PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); - PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); - PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); - // Read only part - PB.LoopStartPos = (PB.LoopStartPos << 16) | (PB.LoopStartPos >> 16); - PB.Length = (PB.Length << 16) | (PB.Length >> 16); - PB.StartAddr = (PB.StartAddr << 16) | (PB.StartAddr >> 16); - PB.UnkAddr = (PB.UnkAddr << 16) | (PB.UnkAddr >> 16); -} - -void ZeldaUCode::WritebackVoicePB(u32 _Addr, ZeldaVoicePB& PB) -{ - u16 *memory = (u16*)Memory::GetPointer(_Addr); - - // Word swap all 32-bit variables. - PB.RestartPos = (PB.RestartPos << 16) | (PB.RestartPos >> 16); - PB.CurAddr = (PB.CurAddr << 16) | (PB.CurAddr >> 16); - PB.RemLength = (PB.RemLength << 16) | (PB.RemLength >> 16); - - // Perform byteswap - // Only the first 0x100 bytes are written back - for (int i = 0; i < (0x100 / 2); i++) - memory[i] = Common::swap16(((u16*)&PB)[i]); -} - -int ZeldaUCode::ConvertRatio(int pb_ratio) -{ - return pb_ratio * 16; -} - -int ZeldaUCode::SizeForResampling(ZeldaVoicePB &PB, int size) -{ - // This is the little calculation at the start of every sample decoder - // in the ucode. - return (PB.CurSampleFrac + size * ConvertRatio(PB.RatioInt)) >> 16; -} - -// Simple resampler, linear interpolation. -// Any future state should be stored in PB.raw[0x3c to 0x3f]. -// In must point 4 samples into a buffer. -void ZeldaUCode::Resample(ZeldaVoicePB &PB, int size, s16 *in, s32 *out, bool do_resample) -{ - if (!do_resample) - { - memcpy(out, in, size * sizeof(int)); - return; - } - - for (int i = 0; i < 4; i++) - { - in[i - 4] = (s16)PB.ResamplerOldData[i]; - } - - int ratio = ConvertRatio(PB.RatioInt); - int in_size = SizeForResampling(PB, size); - - int position = PB.CurSampleFrac; - for (int i = 0; i < size; i++) - { - int int_pos = (position >> 16); - int frac = ((position & 0xFFFF) >> 1); - out[i] = (in[int_pos - 3] * (frac ^ 0x7FFF) + in[int_pos - 2] * frac) >> 15; - position += ratio; - } - - for (int i = 0; i < 4; i++) - { - PB.ResamplerOldData[i] = (u16)(s16)in[in_size - 4 + i]; - } - PB.CurSampleFrac = position & 0xFFFF; -} - -static void UpdateSampleCounters10(ZeldaVoicePB &PB) -{ - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + (PB.RestartPos << 1); - PB.ReachedEnd = 0; -} - -void ZeldaUCode::RenderVoice_PCM16(ZeldaVoicePB &PB, s16 *_Buffer, int _Size) -{ - int _RealSize = SizeForResampling(PB, _Size); - u32 rem_samples = _RealSize; - if (PB.KeyOff) - goto clear_buffer; - if (PB.NeedsReset) - { - UpdateSampleCounters10(PB); - for (int i = 0; i < 4; i++) - PB.ResamplerOldData[i] = 0; // Doesn't belong here, but dunno where to do it. - } - if (PB.ReachedEnd) - { - PB.ReachedEnd = 0; -reached_end: - if (!PB.RepeatMode) - { - // One shot - play zeros the rest of the buffer. -clear_buffer: - for (u32 i = 0; i < rem_samples; i++) - *_Buffer++ = 0; - PB.KeyOff = 1; - return; - } - else - { - PB.RestartPos = PB.LoopStartPos; - UpdateSampleCounters10(PB); - } - } - // SetupAccelerator - const s16 *read_ptr = (s16*)GetARAMPointer(PB.CurAddr); - if (PB.RemLength < (u32)rem_samples) - { - // finish-up loop - for (u32 i = 0; i < PB.RemLength; i++) - *_Buffer++ = Common::swap16(*read_ptr++); - rem_samples -= PB.RemLength; - goto reached_end; - } - // main render loop - for (u32 i = 0; i < rem_samples; i++) - *_Buffer++ = Common::swap16(*read_ptr++); - - PB.RemLength -= rem_samples; - if (PB.RemLength == 0) - PB.ReachedEnd = 1; - PB.CurAddr += rem_samples << 1; -} - -static void UpdateSampleCounters8(ZeldaVoicePB &PB) -{ - PB.RemLength = PB.Length - PB.RestartPos; - PB.CurAddr = PB.StartAddr + PB.RestartPos; - PB.ReachedEnd = 0; -} - -void ZeldaUCode::RenderVoice_PCM8(ZeldaVoicePB &PB, s16 *_Buffer, int _Size) -{ - int _RealSize = SizeForResampling(PB, _Size); - u32 rem_samples = _RealSize; - if (PB.KeyOff) - goto clear_buffer; - if (PB.NeedsReset) - { - UpdateSampleCounters8(PB); - for (int i = 0; i < 4; i++) - PB.ResamplerOldData[i] = 0; // Doesn't belong here, but dunno where to do it. - } - if (PB.ReachedEnd) - { -reached_end: - PB.ReachedEnd = 0; - if (!PB.RepeatMode) - { - // One shot - play zeros the rest of the buffer. -clear_buffer: - for (u32 i = 0; i < rem_samples; i++) - *_Buffer++ = 0; - PB.KeyOff = 1; - return; - } - else - { - PB.RestartPos = PB.LoopStartPos; - UpdateSampleCounters8(PB); - } - } - - // SetupAccelerator - const s8 *read_ptr = (s8*)GetARAMPointer(PB.CurAddr); - if (PB.RemLength < (u32)rem_samples) - { - // finish-up loop - for (u32 i = 0; i < PB.RemLength; i++) - *_Buffer++ = (s8)(*read_ptr++) << 8; - rem_samples -= PB.RemLength; - goto reached_end; - } - // main render loop - for (u32 i = 0; i < rem_samples; i++) - *_Buffer++ = (s8)(*read_ptr++) << 8; - - PB.RemLength -= rem_samples; - if (PB.RemLength == 0) - PB.ReachedEnd = 1; - PB.CurAddr += rem_samples; -} - -template -void PrintObject(const T &Obj) -{ - std::stringstream ss; - u8 *o = (u8 *)&Obj; - - // If this miscompiles, adjust the size of - // ZeldaVoicePB to 0x180 bytes (0xc0 shorts). - static_assert(sizeof(ZeldaVoicePB) == 0x180, "ZeldaVoicePB incorrectly defined."); - - ss << std::hex; - for (size_t i = 0; i < sizeof(T); i++) - { - if ((i & 1) == 0) - ss << ' '; - ss.width(2); - ss.fill('0'); - ss << Common::swap16(o[i]); - } - - DEBUG_LOG(DSPHLE, "AFC PB:%s", ss.str().c_str()); -} - -void ZeldaUCode::RenderVoice_AFC(ZeldaVoicePB &PB, s16 *_Buffer, int _Size) -{ - // TODO: Compare mono, stereo and surround samples -#if defined DEBUG || defined DEBUGFAST - PrintObject(PB); -#endif - - int _RealSize = SizeForResampling(PB, _Size); - - // initialize "decoder" if the sample is played the first time - if (PB.NeedsReset != 0) - { - // This is 0717_ReadOutPBStuff - // increment 4fb - // zelda: - // perhaps init or "has played before" - PB.CurBlock = 0x00; - PB.YN2 = 0x00; // history1 - PB.YN1 = 0x00; // history2 - - // Length in samples. - PB.RemLength = PB.Length; - // Copy ARAM addr from r to rw area. - PB.CurAddr = PB.StartAddr; - PB.ReachedEnd = 0; - PB.CurSampleFrac = 0; - - for (int i = 0; i < 4; i++) - PB.ResamplerOldData[i] = 0; - } - - if (PB.KeyOff != 0) // 0747 early out... i dunno if this can happen because we filter it above - { - for (int i = 0; i < _RealSize; i++) - *_Buffer++ = 0; - return; - } - - // Round upwards how many samples we need to copy, 0759 - // u32 frac = NumberOfSamples & 0xF; - // NumberOfSamples = (NumberOfSamples + 0xf) >> 4; // i think the lower 4 are the fraction - - const u8 *source; - u32 ram_mask = 1024 * 1024 * 16 - 1; - if (IsDMAVersion()) - { - source = Memory::GetPointer(m_dma_base_addr); - ram_mask = 1024 * 1024 * 64 - 1; - } - else - { - source = DSP::GetARAMPtr(); - } - - int sampleCount = 0; // must be above restart. - -restart: - if (PB.ReachedEnd) - { - PB.ReachedEnd = 0; - - if ((PB.RepeatMode == 0) || (PB.StopOnSilence != 0)) - { - PB.KeyOff = 1; - PB.RemLength = 0; - PB.CurAddr = PB.StartAddr + PB.RestartPos + PB.Length; - - while (sampleCount < _RealSize) - _Buffer[sampleCount++] = 0; - return; - } - else - { - //AFC looping - // The loop start pos is incorrect? (Fixed?), so samples will loop a bit wrong. - // this fixes the intro music in ZTP. - PB.RestartPos = PB.LoopStartPos; - PB.RemLength = PB.Length - PB.RestartPos; - // see DSP_UC_Zelda.txt line 2817 - PB.CurAddr = ((((((PB.LoopStartPos >> 4) & 0xffff0000)*PB.Format)<<16)+ - (((PB.LoopStartPos >> 4) & 0xffff)*PB.Format))+PB.StartAddr) & 0xffffffff; - - // Hmm, this shouldn't be reversed .. or should it? Is it different between versions of the ucode? - // -> it has to be reversed in ZTP, otherwise intro music is broken... - PB.YN1 = PB.LoopYN2; - PB.YN2 = PB.LoopYN1; - } - } - - short outbuf[16] = {0}; - u16 prev_yn1 = PB.YN1; - u16 prev_yn2 = PB.YN2; - u32 prev_addr = PB.CurAddr; - - // Prefill the decode buffer. - AFCdecodebuffer(m_afc_coef_table, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format); - PB.CurAddr += PB.Format; // 9 or 5 - - u32 SamplePosition = PB.Length - PB.RemLength; - while (sampleCount < _RealSize) - { - _Buffer[sampleCount] = outbuf[SamplePosition & 15]; - sampleCount++; - - SamplePosition++; - PB.RemLength--; - if (PB.RemLength == 0) - { - PB.ReachedEnd = 1; - goto restart; - } - - // Need new samples! - if ((SamplePosition & 15) == 0) - { - prev_yn1 = PB.YN1; - prev_yn2 = PB.YN2; - prev_addr = PB.CurAddr; - - AFCdecodebuffer(m_afc_coef_table, (char*)(source + (PB.CurAddr & ram_mask)), outbuf, (short*)&PB.YN2, (short*)&PB.YN1, PB.Format); - PB.CurAddr += PB.Format; // 9 or 5 - } - } - - // Here we should back off to the previous addr/yn1/yn2, since we didn't consume the full last block. - // We'll re-decode it the next time around. - PB.YN2 = prev_yn2; - PB.YN1 = prev_yn1; - PB.CurAddr = prev_addr; - - PB.NeedsReset = 0; - // PB.CurBlock = 0x10 - (PB.LoopStartPos & 0xf); - // write back - // NumberOfSamples = (NumberOfSamples << 4) | frac; // missing fraction - - // i think pTest[0x3a] and pTest[0x3b] got an update after you have decoded some samples... - // just decrement them with the number of samples you have played - // and increase the ARAM Offset in pTest[0x38], pTest[0x39] - - // end of block (Zelda 03b2) -} - -void Decoder21_ReadAudio(ZeldaVoicePB &PB, int size, s16 *_Buffer); - -// Researching what's actually inside the mysterious 0x21 case -// 0x21 seems to really just be reading raw 16-bit audio from RAM (not ARAM). -// The rules seem to be quite different, though. -// It's used for streaming, not for one-shot or looped sample playback. -void ZeldaUCode::RenderVoice_Raw(ZeldaVoicePB &PB, s16 *_Buffer, int _Size) -{ - // Decoder0x21 starts here. - u32 _RealSize = SizeForResampling(PB, _Size); - - // Decoder0x21Core starts here. - u32 AX0 = _RealSize; - - // ERROR_LOG(DSPHLE, "0x21 volume mode: %i , stop: %i ", PB.VolumeMode, PB.StopOnSilence); - - // The PB.StopOnSilence check is a hack, we should check the buffers and enter this - // only when the buffer is completely 0 (i.e. when the music has finished fading out) - if (PB.StopOnSilence || PB.RemLength < (u32)_RealSize) - { - WARN_LOG(DSPHLE, "Raw: END"); - // Let's ignore this entire case since it doesn't seem to happen - // in Zelda, since Length is set to 0xF0000000 - // blah - // blah - // readaudio - // blah - PB.RemLength = 0; - PB.KeyOff = 1; - } - - PB.RemLength -= _RealSize; - - u64 ACC0 = (u32)(PB.raw[0x8a ^ 1] << 16); // 0x8a 0ad5, yes it loads a, not b - u64 ACC1 = (u32)(PB.raw[0x34 ^ 1] << 16); // 0x34 - - // ERROR_LOG(DSPHLE, "%08x %08x", (u32)ACC0, (u32)ACC1); - - ACC0 -= ACC1; - - PB.Unk36[0] = (u16)(ACC0 >> 16); - - ACC0 -= AX0 << 16; - - if ((s64)ACC0 < 0) - { - // ERROR_LOG(DSPHLE, "Raw loop: ReadAudio size = %04x 34:%04x %08x", PB.Unk36[0], PB.raw[0x34 ^ 1], (int)ACC0); - Decoder21_ReadAudio(PB, PB.Unk36[0], _Buffer); - - ACC0 = -(s64)ACC0; - _Buffer += PB.Unk36[0]; - - PB.raw[0x34 ^ 1] = 0; - - PB.StartAddr = PB.LoopStartPos; - - Decoder21_ReadAudio(PB, (int)(ACC0 >> 16), _Buffer); - return; - } - - Decoder21_ReadAudio(PB, _RealSize, _Buffer); -} - -void Decoder21_ReadAudio(ZeldaVoicePB &PB, int size, s16* _Buffer) -{ - // 0af6 - if (!size) - return; - -#if 0 - // 0afa - u32 AX1 = (PB.RestartPos >> 16) & 1; // PB.raw[0x34], except that it's part of a dword - // 0b00 - Eh, WTF. - u32 ACC0 = PB.StartAddr + ((PB.RestartPos >> 16) << 1) - 2*AX1; - u32 ACC1 = (size << 16) + 0x20000; - // All this trickery, and more, seems to be to align the DMA, which - // we really don't care about. So let's skip it. See the #else. - -#else - // ERROR_LOG(DSPHLE, "ReadAudio: %08x %08x", PB.StartAddr, PB.raw[0x34 ^ 1]); - u32 ACC0 = PB.StartAddr + (PB.raw[0x34 ^ 1] << 1); - u32 ACC1 = (size << 16); -#endif - // ACC0 is the address - // ACC1 is the read size - - const u16* src = (u16*)Memory::GetPointer(ACC0 & Memory::RAM_MASK); - - for (u32 i = 0; i < (ACC1 >> 16); i++) - { - _Buffer[i] = Common::swap16(src[i]); - } - - PB.raw[0x34 ^ 1] += size; -} - - -void ZeldaUCode::RenderAddVoice(ZeldaVoicePB &PB, s32* _LeftBuffer, s32* _RightBuffer, int _Size) -{ - if (PB.IsBlank) - { - s32 sample = (s32)(s16)PB.FixedSample; - for (int i = 0; i < _Size; i++) - m_voice_buffer[i] = sample; - - goto ContinueWithBlock; // Yes, a goto. Yes, it's evil, but it makes the flow look much more like the DSP code. - } - - // XK: Use this to disable MIDI music (GREAT for testing). Also kills some sound FX. - //if (PB.SoundType == 0x0d00) - //{ - // PB.NeedsReset = 0; - // return; - //} - - // The Resample calls actually don't resample yet. - - // ResampleBuffer corresponds to 0x0580 in ZWW ucode. - // VoiceBuffer corresponds to 0x0520. - - // First jump table at ZWW: 2a6 - switch (PB.Format) - { - case 0x0005: // AFC with extra low bitrate (32:5 compression). - case 0x0009: // AFC with normal bitrate (32:9 compression). - RenderVoice_AFC(PB, m_resample_buffer + 4, _Size); - Resample(PB, _Size, m_resample_buffer + 4, m_voice_buffer, true); - break; - - case 0x0008: // PCM8 - normal PCM 8-bit audio. Used in Mario Kart DD + very little in Zelda WW. - RenderVoice_PCM8(PB, m_resample_buffer + 4, _Size); - Resample(PB, _Size, m_resample_buffer + 4, m_voice_buffer, true); - break; - - case 0x0010: // PCM16 - normal PCM 16-bit audio. - RenderVoice_PCM16(PB, m_resample_buffer + 4, _Size); - Resample(PB, _Size, m_resample_buffer + 4, m_voice_buffer, true); - break; - - case 0x0020: - // Normally, this shouldn't resample, it should just decode directly - // to the output buffer. However, (if we ever see this sound type), we'll - // have to resample anyway since we're running at a different sample rate. - - RenderVoice_Raw(PB, m_resample_buffer + 4, _Size); - Resample(PB, _Size, m_resample_buffer + 4, m_voice_buffer, true); - break; - - case 0x0021: - // Raw sound from RAM. Important for Zelda WW. Cutscenes use the music - // to let the game know they ended - RenderVoice_Raw(PB, m_resample_buffer + 4, _Size); - Resample(PB, _Size, m_resample_buffer + 4, m_voice_buffer, true); - break; - - default: - // Second jump table - // TODO: Cases to find examples of: - // -0x0002 - // -0x0003 - // -0x0006 - // -0x000a - switch (PB.Format) - { - // Synthesized sounds - case 0x0003: WARN_LOG(DSPHLE, "PB Format 0x03 used!"); - case 0x0000: // Example: Magic meter filling up in ZWW - RenderSynth_RectWave(PB, m_voice_buffer, _Size); - break; - - case 0x0001: // Example: "Denied" sound when trying to pull out a sword indoors in ZWW - RenderSynth_SawWave(PB, m_voice_buffer, _Size); - break; - - case 0x0006: - WARN_LOG(DSPHLE, "Synthesizing 0x0006 (constant sound)"); - RenderSynth_Constant(PB, m_voice_buffer, _Size); - break; - - // These are more "synth" formats - square wave, saw wave etc. - case 0x0002: - WARN_LOG(DSPHLE, "PB Format 0x02 used!"); - break; - - case 0x0004: // Example: Big Pikmin onion mothership landing/building a bridge in Pikmin - case 0x0007: // Example: "success" SFX in Pikmin 1, Pikmin 2 in a cave, not sure what sound it is. - case 0x000b: // Example: SFX in area selection menu in Pikmin - case 0x000c: // Example: beam of death/yellow force-field in Temple of the Gods, ZWW - RenderSynth_WaveTable(PB, m_voice_buffer, _Size); - break; - - default: - // TODO: Implement general decoder here - memset(m_voice_buffer, 0, _Size * sizeof(s32)); - ERROR_LOG(DSPHLE, "Unknown MixAddVoice format in zelda %04x", PB.Format); - break; - } - } - -ContinueWithBlock: - - if (PB.FilterEnable) - { // 0x04a8 - for (int i = 0; i < _Size; i++) - { - // TODO: Apply filter from ZWW: 0c84_FilterBufferInPlace - } - } - - for (int i = 0; i < _Size; i++) - { - // TODO? - } - - // Apply volume. There are two different modes. - if (PB.VolumeMode != 0) - { - // Complex volume mode. Let's see what we can do. - if (PB.StopOnSilence) - { - PB.raw[0x2b] = PB.raw[0x2a] >> 1; - if (PB.raw[0x2b] == 0) - { - PB.KeyOff = 1; - } - } - - short AX0L = PB.raw[0x28] >> 8; - short AX0H = PB.raw[0x28] & 0x7F; - short AX1L = AX0L ^ 0x7F; - short AX1H = AX0H ^ 0x7F; - AX0L = m_misc_table[0x200 + AX0L]; - AX0H = m_misc_table[0x200 + AX0H]; - AX1L = m_misc_table[0x200 + AX1L]; - AX1H = m_misc_table[0x200 + AX1H]; - - short b00[20]; - b00[0] = AX1L * AX1H >> 16; - b00[1] = AX0L * AX1H >> 16; - b00[2] = AX0H * AX1L >> 16; - b00[3] = AX0L * AX0H >> 16; - - for (int i = 0; i < 4; i++) - { - b00[i + 4] = (s16)b00[i] * (s16)PB.raw[0x2a] >> 16; - } - - int prod = ((s16)PB.raw[0x2a] * (s16)PB.raw[0x29] * 2) >> 16; - for (int i = 0; i < 4; i++) - { - b00[i + 8] = (s16)b00[i + 4] * prod; - } - - // ZWW 0d34 - - int diff = (s16)PB.raw[0x2b] - (s16)PB.raw[0x2a]; - PB.raw[0x2a] = PB.raw[0x2b]; - - for (int i = 0; i < 4; i++) - { - b00[i + 0xc] = (unsigned short)b00[i] * diff >> 16; - } - - for (int i = 0; i < 4; i++) - { - b00[i + 0x10] = (s16)b00[i + 0xc] * PB.raw[0x29]; - } - - for (int count = 0; count < 8; count++) - { - // The 8 buffers to mix to: 0d00, 0d60, 0f40 0ca0 0e80 0ee0 0c00 0c50 - // We just mix to the first two and call it stereo :p - int value = b00[0x4 + count]; - //int delta = b00[0xC + count] << 11; // Unused? - - int ramp = value << 16; - for (int i = 0; i < _Size; i++) - { - int unmixed_audio = m_voice_buffer[i]; - switch (count) - { - case 0: _LeftBuffer[i] += (u64)unmixed_audio * ramp >> 29; break; - case 1: _RightBuffer[i] += (u64)unmixed_audio * ramp >> 29; break; - } - } - } - } - else - { - // ZWW 0355 - if (PB.StopOnSilence) - { - int sum = 0; - int addr = 0x0a; - for (int i = 0; i < 6; i++) - { - u16 value = PB.raw[addr]; - addr--; - value >>= 1; - PB.raw[addr] = value; - sum += value; - addr += 5; - } - - if (sum == 0) - { - PB.KeyOff = 1; - } - } - - // Seems there are 6 temporary output buffers. - for (int count = 0; count < 6; count++) - { - int addr = 0x08; - - // we'll have to keep a map of buffers I guess... - u16 dest_buffer_address = PB.raw[addr++]; - - bool mix = dest_buffer_address ? true : false; - - u16 vol2 = PB.raw[addr++]; - u16 vol1 = PB.raw[addr++]; - - int delta = (vol2 - vol1) << 11; - - addr--; - - u32 ramp = vol1 << 16; - if (mix) - { - // 0ca9_RampedMultiplyAddBuffer - for (int i = 0; i < _Size; i++) - { - int value = m_voice_buffer[i]; - - // TODO - add to buffer specified by dest_buffer_address - switch (count) - { - // These really should be 32. - case 0: _LeftBuffer[i] += (u64)value * ramp >> 29; break; - case 1: _RightBuffer[i] += (u64)value * ramp >> 29; break; - } - - if (((i & 1) == 0) && i < 64) - { - ramp += delta; - } - } - if (_Size < 32) - { - ramp += delta * (_Size - 32); - } - } - // Update the PB with the volume actually reached. - PB.raw[addr++] = ramp >> 16; - - addr++; - } - } - // 03b2, this is the reason of using PB.NeedsReset. Seems to be necessary for SMG, and maybe other games. - if (PB.IsBlank == 0) - { - PB.NeedsReset = 0; - } -} - -void ZeldaUCode::MixAudio() -{ - const int BufferSamples = 5 * 16; - - // Final mix buffers - memset(m_left_buffer, 0, BufferSamples * sizeof(s32)); - memset(m_right_buffer, 0, BufferSamples * sizeof(s32)); - - // For each PB... - for (u32 i = 0; i < m_num_voices; i++) - { - if (!IsLightVersion()) - { - u32 flags = m_sync_flags[(i >> 4) & 0xF]; - if (!(flags & 1 << (15 - (i & 0xF)))) - continue; - } - - ZeldaVoicePB pb; - ReadVoicePB(m_voice_pbs_addr + (i * 0x180), pb); - - if (pb.Status == 0) - continue; - if (pb.KeyOff != 0) - continue; - - RenderAddVoice(pb, m_left_buffer, m_right_buffer, BufferSamples); - WritebackVoicePB(m_voice_pbs_addr + (i * 0x180), pb); - } - - // Post processing, final conversion. - s16* left_buffer = (s16*)HLEMemory_Get_Pointer(m_left_buffers_addr); - s16* right_buffer = (s16*)HLEMemory_Get_Pointer(m_right_buffers_addr); - left_buffer += m_current_buffer * BufferSamples; - right_buffer += m_current_buffer * BufferSamples; - for (int i = 0; i < BufferSamples; i++) - { - s32 left = m_left_buffer[i]; - s32 right = m_right_buffer[i]; - - MathUtil::Clamp(&left, -32768, 32767); - left_buffer[i] = Common::swap16((short)left); - - MathUtil::Clamp(&right, -32768, 32767); - right_buffer[i] = Common::swap16((short)right); - } -} From 8f3302419b20c881ab6a9c0185bd7f05ceaf8c5f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 27 Apr 2014 13:03:32 +0200 Subject: [PATCH 03/59] ZeldaHLE: Rip out more code, only keep normal version support and one CRC --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 20 -- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 263 ++----------------- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 54 ---- 3 files changed, 24 insertions(+), 313 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index 24e3732b61..7d607fd6d1 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -52,27 +52,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) INFO_LOG(DSPHLE, "CRC %08x: AX ucode chosen", crc); return new AXUCode(dsphle, crc); - case 0x6ba3b3ea: // IPL - PAL - case 0x24b22038: // IPL - NTSC/NTSC-JAP - case 0x42f64ac4: // Luigi's Mansion - case 0x4be6a5cb: // AC, Pikmin - INFO_LOG(DSPHLE, "CRC %08x: JAC (early Zelda) ucode chosen", crc); - return new ZeldaUCode(dsphle, crc); - - case 0x6CA33A6D: // DK Jungle Beat case 0x86840740: // Zelda WW - US - case 0x56d36052: // Mario Sunshine - case 0x2fcdf1ec: // Mario Kart, Zelda 4 Swords - case 0x267fd05a: // Pikmin PAL - INFO_LOG(DSPHLE, "CRC %08x: Zelda ucode chosen", crc); - return new ZeldaUCode(dsphle, crc); - - // Wii CRCs - case 0xb7eb9a9c: // Wii Pikmin - PAL - case 0xeaeb38cc: // Wii Pikmin 2 - PAL - case 0x6c3f6f94: // Zelda TP - PAL - case 0xd643001f: // Mario Galaxy - PAL / Wii DK Jungle Beat - PAL - INFO_LOG(DSPHLE, "CRC %08x: Zelda Wii ucode chosen\n", crc); return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 72a8b0d42e..07afb830f7 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -2,9 +2,20 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -// Games that uses this UCode: -// Zelda: The Windwaker, Mario Sunshine, Mario Kart, Twilight Princess, -// Super Mario Galaxy +// Games that uses this UCode (exhaustive list): +// * Animal Crossing (type ????, CRC ????) +// * Donkey Kong Jungle Beat (type ????, CRC ????) +// * IPL (type ????, CRC ????) +// * Luigi's Mansion (type ????, CRC ????) +// * Mario Kary: Double Dash!! (type ????, CRC ????) +// * Pikmin (type ????, CRC ????) +// * Pikmin 2 (type ????, CRC ????) +// * Super Mario Galaxy (type ????, CRC ????) +// * Super Mario Galaxy 2 (type ????, CRC ????) +// * Super Mario Sunshine (type ????, CRC ????) +// * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) +// * The Legend of Zelda: The Wind Waker (type Normal, CRC 86840740) +// * The Legend of Zelda: Twilight Princess (type ????, CRC ????) #include "Core/ConfigManager.h" #include "Core/HW/DSP.h" @@ -12,7 +23,6 @@ #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/DSPHLE/UCodes/Zelda.h" - ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) : UCodeInterface(dsphle, crc), m_sync_in_progress(false), @@ -28,19 +38,9 @@ ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) m_step(0), m_read_offset(0) { - DEBUG_LOG(DSPHLE, "UCode_Zelda - add boot mails for handshake"); - - if (IsLightVersion()) - { - DEBUG_LOG(DSPHLE, "Luigi Stylee!"); - m_mail_handler.PushMail(0x88881111); - } - else - { - m_mail_handler.PushMail(DSP_INIT); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_mail_handler.PushMail(0xF3551111); // handshake - } + m_mail_handler.PushMail(DSP_INIT); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_mail_handler.PushMail(0xF3551111); // handshake } ZeldaUCode::~ZeldaUCode() @@ -50,12 +50,6 @@ ZeldaUCode::~ZeldaUCode() void ZeldaUCode::Update() { - if (!IsLightVersion()) - { - if (m_mail_handler.GetNextMail() == DSP_FRAME_END) - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - } - if (NeedsResumeMail()) { m_mail_handler.PushMail(DSP_RESUME); @@ -65,169 +59,6 @@ void ZeldaUCode::Update() void ZeldaUCode::HandleMail(u32 mail) { - if (IsLightVersion()) - HandleMail_LightVersion(mail); - else if (IsSMSVersion()) - HandleMail_SMSVersion(mail); - else - HandleMail_NormalVersion(mail); -} - -void ZeldaUCode::HandleMail_LightVersion(u32 mail) -{ - //ERROR_LOG(DSPHLE, "Light version mail %08X, list in progress: %s, step: %i/%i", - // mail, m_list_in_progress ? "yes":"no", m_step, m_num_steps); - - if (m_sync_cmd_pending) - { - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - - // TODO(delroth): Mix audio. - - m_current_buffer++; - - if (m_current_buffer == m_num_buffers) - { - m_sync_cmd_pending = false; - DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync"); - } - return; - } - - if (!m_list_in_progress) - { - switch ((mail >> 24) & 0x7F) - { - case 0x00: m_num_steps = 1; break; // dummy - case 0x01: m_num_steps = 5; break; // DsetupTable - case 0x02: m_num_steps = 3; break; // DsyncFrame - - default: - { - m_num_steps = 0; - PanicAlert("Zelda uCode (light version): unknown/unsupported command %02X", (mail >> 24) & 0x7F); - } - return; - } - - m_list_in_progress = true; - m_step = 0; - } - - if (m_step >= sizeof(m_buffer) / 4) - PanicAlert("m_step out of range"); - - ((u32*)m_buffer)[m_step] = mail; - m_step++; - - if (m_step >= m_num_steps) - { - ExecuteList(); - m_list_in_progress = false; - } -} - -void ZeldaUCode::HandleMail_SMSVersion(u32 mail) -{ - if (m_sync_in_progress) - { - if (m_sync_cmd_pending) - { - m_sync_flags[(m_num_sync_mail << 1) ] = mail >> 16; - m_sync_flags[(m_num_sync_mail << 1) + 1] = mail & 0xFFFF; - - m_num_sync_mail++; - if (m_num_sync_mail == 2) - { - m_num_sync_mail = 0; - m_sync_in_progress = false; - - // TODO(delroth): Mix audio. - - m_current_buffer++; - - m_mail_handler.PushMail(DSP_SYNC); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_mail_handler.PushMail(0xF355FF00 | m_current_buffer); - - if (m_current_buffer == m_num_buffers) - { - m_mail_handler.PushMail(DSP_FRAME_END); - // DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - - m_sync_cmd_pending = false; - } - } - } - else - { - m_sync_in_progress = false; - } - - return; - } - - if (m_list_in_progress) - { - if (m_step >= sizeof(m_buffer) / 4) - PanicAlert("m_step out of range"); - - ((u32*)m_buffer)[m_step] = mail; - m_step++; - - if (m_step >= m_num_steps) - { - ExecuteList(); - m_list_in_progress = false; - } - - return; - } - - // Here holds: m_sync_in_progress == false && m_list_in_progress == false - - if (mail == 0) - { - m_sync_in_progress = true; - m_num_sync_mail = 0; - } - else if ((mail >> 16) == 0) - { - m_list_in_progress = true; - m_num_steps = mail; - m_step = 0; - } - else if ((mail >> 16) == 0xCDD1) // A 0xCDD1000X mail should come right after we send a DSP_SYNCEND mail - { - // The low part of the mail tells the operation to perform - // Seeing as every possible operation number halts the uCode, - // except 3, that thing seems to be intended for debugging - switch (mail & 0xFFFF) - { - case 0x0003: // Do nothing - return; - - case 0x0000: // Halt - case 0x0001: // Dump memory? and halt - case 0x0002: // Do something and halt - WARN_LOG(DSPHLE, "Zelda uCode(SMS version): received halting operation %04X", mail & 0xFFFF); - return; - - default: // Invalid (the real ucode would likely crash) - WARN_LOG(DSPHLE, "Zelda uCode(SMS version): received invalid operation %04X", mail & 0xFFFF); - return; - } - } - else - { - WARN_LOG(DSPHLE, "Zelda uCode (SMS version): unknown mail %08X", mail); - } -} - -void ZeldaUCode::HandleMail_NormalVersion(u32 mail) -{ - // WARN_LOG(DSPHLE, "Zelda uCode: Handle mail %08X", mail); - if (m_upload_setup_in_progress) // evaluated first! { PrepareBootUCode(mail); @@ -259,10 +90,7 @@ void ZeldaUCode::HandleMail_NormalVersion(u32 mail) if (m_current_buffer == m_num_buffers) { - if (!IsDMAVersion()) // this is a hack... without it Pikmin 1 Wii/ Zelda TP Wii mail-s stopped - m_mail_handler.PushMail(DSP_FRAME_END); - //g_dspInitialize.pGenerateDSPInterrupt(); - + m_mail_handler.PushMail(DSP_FRAME_END); m_sync_cmd_pending = false; } } @@ -343,10 +171,8 @@ void ZeldaUCode::HandleMail_NormalVersion(u32 mail) } } -// zelda debug ..803F6418 void ZeldaUCode::ExecuteList() { - // begin with the list m_read_offset = 0; u32 cmd_mail = Read32(); @@ -354,79 +180,38 @@ void ZeldaUCode::ExecuteList() u32 sync; u32 extra_data = cmd_mail & 0xFFFF; - if (IsLightVersion()) - sync = 0x62 + (command << 1); // seen in DSP_UC_Luigi.txt - else - sync = cmd_mail >> 16; - - DEBUG_LOG(DSPHLE, "=============================================================================="); - DEBUG_LOG(DSPHLE, "Zelda UCode - execute dlist (command: 0x%04x : sync: 0x%04x)", command, sync); + sync = cmd_mail >> 16; switch (command) { - // dummy case 0x00: break; - // DsetupTable ... zelda ww jumps to 0x0095 case 0x01: Read32(); Read32(); Read32(); Read32(); break; - // SyncFrame ... zelda ww jumps to 0x0243 case 0x02: Read32(); Read32(); - if (IsLightVersion()) - break; - else - return; + return; - - // Simply sends the sync messages case 0x03: break; -/* case 0x04: break; // dunno ... zelda ww jmps to 0x0580 - case 0x05: break; // dunno ... zelda ww jmps to 0x0592 - case 0x06: break; // dunno ... zelda ww jmps to 0x0469 - case 0x07: break; // dunno ... zelda ww jmps to 0x044d - case 0x08: break; // Mixer ... zelda ww jmps to 0x0485 - case 0x09: break; // dunno ... zelda ww jmps to 0x044d - */ - - // DsetDolbyDelay ... zelda ww jumps to 0x00b2 case 0x0d: - { - u32 tmp = Read32(); - DEBUG_LOG(DSPHLE, "DSetDolbyDelay"); - DEBUG_LOG(DSPHLE, "DOLBY2_DELAY_BUF (size 0x960): 0x%08x", tmp); - } + Read32(); break; - // This opcode, in the SMG ucode, sets the base address for audio data transfers from main memory (using DMA). - // In the Zelda ucode, it is dummy, because this ucode uses accelerator for audio data transfers. case 0x0e: Read32(); break; - // default ... zelda ww jumps to 0x0043 default: PanicAlert("Zelda UCode - unknown command: %x (size %i)", command, m_num_steps); break; } - // sync, we are ready - if (IsLightVersion()) - { - if (m_sync_cmd_pending) - m_mail_handler.PushMail(0x80000000 | m_num_buffers); // after CMD_2 - else - m_mail_handler.PushMail(0x80000000 | sync); // after CMD_0, CMD_1 - } - else - { - m_mail_handler.PushMail(DSP_SYNC); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_mail_handler.PushMail(0xF3550000 | sync); - } + m_mail_handler.PushMail(DSP_SYNC); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_mail_handler.PushMail(0xF3550000 | sync); } u32 ZeldaUCode::GetUpdateMs() diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 5f65008c35..c812f2b324 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -15,9 +15,6 @@ public: u32 GetUpdateMs() override; void HandleMail(u32 mail) override; - void HandleMail_LightVersion(u32 mail); - void HandleMail_SMSVersion(u32 mail); - void HandleMail_NormalVersion(u32 mail); void Update() override; void DoState(PointerWrap &p) override; @@ -30,57 +27,6 @@ public: } private: - // These map CRC to behavior. - - // DMA version - // - sound data transferred using DMA instead of accelerator - bool IsDMAVersion() const - { - switch (m_crc) - { - case 0xb7eb9a9c: // Wii Pikmin - PAL - case 0xeaeb38cc: // Wii Pikmin 2 - PAL - case 0x6c3f6f94: // Wii Zelda TP - PAL - case 0xD643001F: // Super Mario Galaxy - return true; - default: - return false; - } - } - - // Light version - // - slightly different communication protocol (no list begin mail) - // - exceptions and interrupts not used - bool IsLightVersion() const - { - switch (m_crc) - { - case 0x6ba3b3ea: // IPL - PAL - case 0x24b22038: // IPL - NTSC/NTSC-JAP - case 0x42f64ac4: // Luigi's Mansion - case 0x4be6a5cb: // AC, Pikmin NTSC - return true; - default: - return false; - } - } - - // SMS version - // - sync mails are sent every frame, not every 16 PBs - // (named SMS because it's used by Super Mario Sunshine - // and I couldn't find a better name) - bool IsSMSVersion() const - { - switch (m_crc) - { - case 0x56d36052: // Super Mario Sunshine - case 0x267fd05a: // Pikmin PAL - return true; - default: - return false; - } - } - bool m_sync_in_progress; u32 m_max_voice; u32 m_sync_flags[16]; From a770cc0778c1e3be03dca9a9a92a63fc4414481f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 20 Nov 2014 05:02:09 +0100 Subject: [PATCH 04/59] DSPHLE MailHandler: Synchronize reads and interrupts This change is meant to solve the following problem: how to translate the following snippet to DSPHLE: SendInterruptAndWaitRead(MAIL_A); SendAndWaitRead(MAIL_B); SendInterruptAndWaitRead(MAIL_C); This should cause the following actions on the CPU side: ---> Woken up by interrupt Reads MAIL_A Reads MAIL_B <--- Exits interrupt handler ---> Woken up by interrupt Reads MAIL_C <--- But with the current DSPHLE mail support, the following would happen because the "AndWaitRead" part is not supported: ---> Woken up by interrupt Reads MAIL_A Reads MAIL_B <--- Exits interrupt handler [Never gets the second interrupt since it was triggered at the same time as the first one! Misses MAIL_C.] This changes fixes the issue by storing two values in the mail queue on the DSP side: the value of the mail itself, and whether a read of that mail should trigger a DSP interrupt. If nothing is in the queue yet and an interrupt is requested, just trigger the interrupt. In the present example, the queue will look like this: Mail value Interrupt requested MAIL_A No <-- Interrupt was triggered when pushing the mail to the queue. MAIL_B Yes MAIL_C No When the CPU will read MAIL_B, this will cause MailHandler to trigger the interrupt, which will be handled by the CPU when coming back from the exception handler. MAIL_C is then successfully read. --- Source/Core/Core/HW/DSPHLE/MailHandler.cpp | 46 ++++++++++++++++------ Source/Core/Core/HW/DSPHLE/MailHandler.h | 19 ++------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/MailHandler.cpp b/Source/Core/Core/HW/DSPHLE/MailHandler.cpp index 3334626ac0..18ab8ee4c2 100644 --- a/Source/Core/Core/HW/DSPHLE/MailHandler.cpp +++ b/Source/Core/Core/HW/DSPHLE/MailHandler.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "Common/ChunkFile.h" +#include "Core/HW/DSP.h" #include "Core/HW/DSPHLE/MailHandler.h" CMailHandler::CMailHandler() @@ -14,9 +15,20 @@ CMailHandler::~CMailHandler() Clear(); } -void CMailHandler::PushMail(u32 _Mail) +void CMailHandler::PushMail(u32 _Mail, bool interrupt) { - m_Mails.push(_Mail); + if (interrupt) + { + if (m_Mails.empty()) + { + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + else + { + m_Mails.front().second = true; + } + } + m_Mails.emplace(_Mail, false); DEBUG_LOG(DSP_MAIL, "DSP writes 0x%08x", _Mail); } @@ -25,7 +37,7 @@ u16 CMailHandler::ReadDSPMailboxHigh() // check if we have a mail for the core if (!m_Mails.empty()) { - u16 result = (m_Mails.front() >> 16) & 0xFFFF; + u16 result = (m_Mails.front().first >> 16) & 0xFFFF; return result; } return 0x00; @@ -36,8 +48,15 @@ u16 CMailHandler::ReadDSPMailboxLow() // check if we have a mail for the core if (!m_Mails.empty()) { - u16 result = m_Mails.front() & 0xFFFF; + u16 result = m_Mails.front().first & 0xFFFF; + bool generate_interrupt = m_Mails.front().second; m_Mails.pop(); + + if (generate_interrupt) + { + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + } + return result; } return 0x00; @@ -59,7 +78,7 @@ void CMailHandler::Halt(bool _Halt) if (_Halt) { Clear(); - m_Mails.push(0x80544348); + PushMail(0x80544348); } } @@ -73,21 +92,25 @@ void CMailHandler::DoState(PointerWrap &p) for (int i = 0; i < sz; i++) { u32 mail = 0; + bool interrupt = false; p.Do(mail); - m_Mails.push(mail); + p.Do(interrupt); + m_Mails.emplace(mail, interrupt); } } else // WRITE and MEASURE { - std::queue temp; + std::queue> temp; int sz = (int)m_Mails.size(); p.Do(sz); for (int i = 0; i < sz; i++) { - u32 value = m_Mails.front(); + u32 value = m_Mails.front().first; + bool interrupt = m_Mails.front().second; m_Mails.pop(); p.Do(value); - temp.push(value); + p.Do(interrupt); + temp.emplace(value, interrupt); } if (!m_Mails.empty()) PanicAlert("CMailHandler::DoState - WTF?"); @@ -95,9 +118,10 @@ void CMailHandler::DoState(PointerWrap &p) // Restore queue. for (int i = 0; i < sz; i++) { - u32 value = temp.front(); + u32 value = temp.front().first; + bool interrupt = temp.front().second; temp.pop(); - m_Mails.push(value); + m_Mails.emplace(value, interrupt); } } } diff --git a/Source/Core/Core/HW/DSPHLE/MailHandler.h b/Source/Core/Core/HW/DSPHLE/MailHandler.h index 40ecaec953..41d5f77c89 100644 --- a/Source/Core/Core/HW/DSPHLE/MailHandler.h +++ b/Source/Core/Core/HW/DSPHLE/MailHandler.h @@ -5,6 +5,8 @@ #pragma once #include +#include + #include "Common/CommonTypes.h" class PointerWrap; @@ -15,7 +17,7 @@ public: CMailHandler(); ~CMailHandler(); - void PushMail(u32 _Mail); + void PushMail(u32 _Mail, bool interrupt = false); void Clear(); void Halt(bool _Halt); void DoState(PointerWrap &p); @@ -24,20 +26,7 @@ public: u16 ReadDSPMailboxHigh(); u16 ReadDSPMailboxLow(); - u32 GetNextMail() const - { - if (!m_Mails.empty()) - { - return m_Mails.front(); - } - else - { - // WARN_LOG(DSPHLE, "GetNextMail: No mails"); - return 0; - } - } - private: // mail handler - std::queue m_Mails; + std::queue> m_Mails; }; From d5e177d3f9b73b015468c19fbb0fb673a344e6d0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 20 Nov 2014 05:10:50 +0100 Subject: [PATCH 05/59] INIT UCode: reduce DSP initialization time Push the mail at UCode boot time, not after the first update. This avoids a lot of crazy busy-looping on the CPU side while waiting for the DSP to be initialized. --- Source/Core/Core/HW/DSPHLE/UCodes/INIT.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/INIT.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/INIT.cpp index face5fb454..e6be9773bb 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/INIT.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/INIT.cpp @@ -10,6 +10,7 @@ INITUCode::INITUCode(DSPHLE *dsphle, u32 crc) : UCodeInterface(dsphle, crc) { DEBUG_LOG(DSPHLE, "INITUCode - initialized"); + m_mail_handler.PushMail(0x80544348); } INITUCode::~INITUCode() @@ -22,11 +23,6 @@ void INITUCode::Init() void INITUCode::Update() { - if (m_mail_handler.IsEmpty()) - { - m_mail_handler.PushMail(0x80544348); - // HALT - } } u32 INITUCode::GetUpdateMs() From d9188f12450529f88714bdd40ab1507b22f46c56 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 20 Nov 2014 05:11:47 +0100 Subject: [PATCH 06/59] Zelda HLE: Rewrite the control flow. Now accurate control flow for DAC. Voice rendering is not implemented yet, but framing seems correct. Not expected to work for anything than DAC UCode games. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 375 +++++++++++--------- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 101 ++++-- 2 files changed, 292 insertions(+), 184 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 07afb830f7..a3607360eb 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -2,44 +2,30 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -// Games that uses this UCode (exhaustive list): +// Games that use this UCode (exhaustive list): // * Animal Crossing (type ????, CRC ????) // * Donkey Kong Jungle Beat (type ????, CRC ????) // * IPL (type ????, CRC ????) // * Luigi's Mansion (type ????, CRC ????) -// * Mario Kary: Double Dash!! (type ????, CRC ????) +// * Mario Kart: Double Dash!! (type ????, CRC ????) // * Pikmin (type ????, CRC ????) // * Pikmin 2 (type ????, CRC ????) // * Super Mario Galaxy (type ????, CRC ????) // * Super Mario Galaxy 2 (type ????, CRC ????) // * Super Mario Sunshine (type ????, CRC ????) // * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) -// * The Legend of Zelda: The Wind Waker (type Normal, CRC 86840740) +// * The Legend of Zelda: The Wind Waker (type DAC, CRC 86840740) // * The Legend of Zelda: Twilight Princess (type ????, CRC ????) #include "Core/ConfigManager.h" -#include "Core/HW/DSP.h" #include "Core/HW/DSPHLE/MailHandler.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/DSPHLE/UCodes/Zelda.h" ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) - : UCodeInterface(dsphle, crc), - m_sync_in_progress(false), - m_max_voice(0), - m_num_sync_mail(0), - m_num_voices(0), - m_sync_cmd_pending(false), - m_current_voice(0), - m_current_buffer(0), - m_num_buffers(0), - m_num_steps(0), - m_list_in_progress(false), - m_step(0), - m_read_offset(0) + : UCodeInterface(dsphle, crc) { - m_mail_handler.PushMail(DSP_INIT); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_mail_handler.PushMail(DSP_INIT, true); m_mail_handler.PushMail(0xF3551111); // handshake } @@ -52,11 +38,39 @@ void ZeldaUCode::Update() { if (NeedsResumeMail()) { - m_mail_handler.PushMail(DSP_RESUME); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + m_mail_handler.PushMail(DSP_RESUME, true); } } +u32 ZeldaUCode::GetUpdateMs() +{ + return SConfig::GetInstance().bWii ? 3 : 5; +} + +void ZeldaUCode::DoState(PointerWrap &p) +{ + p.Do(m_mail_current_state); + p.Do(m_mail_expected_cmd_mails); + + p.Do(m_sync_max_voice_id); + p.Do(m_sync_flags); + + p.Do(m_cmd_buffer); + p.Do(m_read_offset); + p.Do(m_write_offset); + p.Do(m_pending_commands_count); + p.Do(m_cmd_can_execute); + + p.Do(m_rendering_requested_frames); + p.Do(m_rendering_voices_per_frame); + p.Do(m_rendering_mram_lbuf_addr); + p.Do(m_rendering_mram_rbuf_addr); + p.Do(m_rendering_curr_frame); + p.Do(m_rendering_curr_voice); + + DoStateShared(p); +} + void ZeldaUCode::HandleMail(u32 mail) { if (m_upload_setup_in_progress) // evaluated first! @@ -65,181 +79,218 @@ void ZeldaUCode::HandleMail(u32 mail) return; } - if (m_sync_in_progress) + switch (m_mail_current_state) { - if (m_sync_cmd_pending) + case MailState::WAITING: + if (mail & 0x80000000) { - u32 n = (mail >> 16) & 0xF; - m_max_voice = (n + 1) << 4; - m_sync_flags[n] = mail & 0xFFFF; - m_sync_in_progress = false; - - m_current_voice = m_max_voice; - - if (m_current_voice >= m_num_voices) + if ((mail >> 16) != 0xCDD1) { - // TODO(delroth): Mix audio. + ERROR_LOG(DSPHLE, "Rendering end mail without prefix CDD1: %08x", + mail); + } - m_current_buffer++; + switch (mail & 0xFFFF) + { + case 1: + NOTICE_LOG(DSPHLE, "UCode being replaced."); + m_upload_setup_in_progress = true; + SetMailState(MailState::HALTED); + break; - m_mail_handler.PushMail(DSP_SYNC); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_mail_handler.PushMail(0xF355FF00 | m_current_buffer); + case 2: + NOTICE_LOG(DSPHLE, "UCode being rebooted to ROM."); + SetMailState(MailState::HALTED); + m_dsphle->SetUCode(UCODE_ROM); + break; - m_current_voice = 0; + case 3: + m_cmd_can_execute = true; + RunPendingCommands(); + break; - if (m_current_buffer == m_num_buffers) - { - m_mail_handler.PushMail(DSP_FRAME_END); - m_sync_cmd_pending = false; - } + default: + NOTICE_LOG(DSPHLE, "Unknown end rendering action. Halting."); + case 0: + NOTICE_LOG(DSPHLE, "UCode asked to halt. Stopping any processing."); + SetMailState(MailState::HALTED); + break; + } + } + else if (!(mail & 0xFFFF)) + { + if (RenderingInProgress()) + { + SetMailState(MailState::RENDERING); + } + else + { + NOTICE_LOG(DSPHLE, "Sync mail (%08x) received when rendering was not active. Halting.", + mail); + SetMailState(MailState::HALTED); } } else { - m_sync_in_progress = false; + SetMailState(MailState::WRITING_CMD); + m_mail_expected_cmd_mails = mail & 0xFFFF; } + break; - return; - } + case MailState::RENDERING: + m_sync_max_voice_id = (((mail >> 16) & 0xF) + 1) << 4; + m_sync_flags[(mail >> 16) & 0xFF] = mail & 0xFFFF; - if (m_list_in_progress) - { - if (m_step >= sizeof(m_buffer) / 4) - PanicAlert("m_step out of range"); + RenderAudio(); + SetMailState(MailState::WAITING); + break; - ((u32*)m_buffer)[m_step] = mail; - m_step++; + case MailState::WRITING_CMD: + Write32(mail); - if (m_step >= m_num_steps) + if (--m_mail_expected_cmd_mails == 0) { - ExecuteList(); - m_list_in_progress = false; + m_pending_commands_count += 1; + SetMailState(MailState::WAITING); + RunPendingCommands(); } + break; - return; - } - - // Here holds: m_sync_in_progress == false && m_list_in_progress == false - - // Zelda-only mails: - // - 0000XXXX - Begin list - // - 00000000, 000X0000 - Sync mails - // - CDD1XXXX - comes after DsyncFrame completed, seems to be debugging stuff - - if (mail == 0) - { - m_sync_in_progress = true; - } - else if ((mail >> 16) == 0) - { - m_list_in_progress = true; - m_num_steps = mail; - m_step = 0; - } - else if ((mail >> 16) == 0xCDD1) // A 0xCDD1000X mail should come right after we send a DSP_FRAME_END mail - { - // The low part of the mail tells the operation to perform - // Seeing as every possible operation number halts the uCode, - // except 3, that thing seems to be intended for debugging - switch (mail & 0xFFFF) - { - case 0x0003: // Do nothing - continue normally - return; - - case 0x0001: // accepts params to either DMA to iram and/or DRAM (used for hotbooting a new ucode) - // TODO find a better way to protect from HLEMixer? - m_upload_setup_in_progress = true; - return; - - case 0x0002: // Let IROM play us off - m_dsphle->SetUCode(UCODE_ROM); - return; - - case 0x0000: // Halt - WARN_LOG(DSPHLE, "Zelda uCode: received halting operation %04X", mail & 0xFFFF); - return; - - default: // Invalid (the real ucode would likely crash) - WARN_LOG(DSPHLE, "Zelda uCode: received invalid operation %04X", mail & 0xFFFF); - return; - } - } - else - { - WARN_LOG(DSPHLE, "Zelda uCode: unknown mail %08X", mail); + case MailState::HALTED: + WARN_LOG(DSPHLE, "Received mail %08x while we're halted.", mail); + break; } } -void ZeldaUCode::ExecuteList() +void ZeldaUCode::RunPendingCommands() { - m_read_offset = 0; - - u32 cmd_mail = Read32(); - u32 command = (cmd_mail >> 24) & 0x7f; - u32 sync; - u32 extra_data = cmd_mail & 0xFFFF; - - sync = cmd_mail >> 16; - - switch (command) + if (RenderingInProgress() || !m_cmd_can_execute) { - case 0x00: break; + // No commands can be ran while audio rendering is in progress or + // waiting for an ACK. + return; + } - case 0x01: - Read32(); Read32(); Read32(); Read32(); + while (m_pending_commands_count) + { + m_pending_commands_count--; + + u32 cmd_mail = Read32(); + u32 command = (cmd_mail >> 24) & 0x7f; + u32 sync = cmd_mail >> 16; + u32 extra_data = cmd_mail & 0xFFFF; + + switch (command) + { + case 0x00: + case 0x03: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0E: + case 0x0F: + // NOP commands. Log anyway in case we encounter a new version + // where these are not NOPs anymore. + NOTICE_LOG(DSPHLE, "Received a NOP command: %d", command); + SendCommandAck(CommandAck::STANDARD, sync); break; - case 0x02: - Read32(); Read32(); + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + // Commands that crash the DAC UCode. Log and enter HALTED mode. + NOTICE_LOG(DSPHLE, "Received a crashy command: %d", command); + SetMailState(MailState::HALTED); return; - case 0x03: break; - - case 0x0d: - Read32(); + // Command 01: TODO: find a name and implement. + case 0x01: + WARN_LOG(DSPHLE, "CMD01: %08x %08x %08x %08x", + Read32(), Read32(), Read32(), Read32()); + m_rendering_voices_per_frame = extra_data; + SendCommandAck(CommandAck::STANDARD, sync); break; - case 0x0e: - Read32(); + // Command 02: starts audio processing. NOTE: this handler uses return, + // not break. This is because it hijacks the mail control flow and + // stops processing of further commands until audio processing is done. + case 0x02: + m_rendering_requested_frames = (cmd_mail >> 16) & 0xFF; + m_rendering_mram_lbuf_addr = Read32(); + m_rendering_mram_rbuf_addr = Read32(); + + m_rendering_curr_frame = 0; + m_rendering_curr_voice = 0; + RenderAudio(); + return; + + // Command 0D: TODO: find a name and implement. + case 0x0D: + WARN_LOG(DSPHLE, "CMD0D: %08x", Read32()); + SendCommandAck(CommandAck::STANDARD, sync); break; default: - PanicAlert("Zelda UCode - unknown command: %x (size %i)", command, m_num_steps); - break; + NOTICE_LOG(DSPHLE, "Received a non-existing command (%d), halting.", command); + SetMailState(MailState::HALTED); + return; + } + } +} + +void ZeldaUCode::SendCommandAck(CommandAck ack_type, u16 sync_value) +{ + u32 ack_mail; + switch (ack_type) + { + case CommandAck::STANDARD: ack_mail = DSP_SYNC; break; + case CommandAck::DONE_RENDERING: ack_mail = DSP_FRAME_END; break; + } + m_mail_handler.PushMail(ack_mail, true); + + if (ack_type == CommandAck::STANDARD) + { + m_mail_handler.PushMail(0xF3550000 | sync_value); + } +} + +void ZeldaUCode::RenderAudio() +{ + WARN_LOG(DSPHLE, "RenderAudio() frame %d/%d voice %d/%d (sync to %d)", + m_rendering_curr_frame, m_rendering_requested_frames, + m_rendering_curr_voice, m_rendering_voices_per_frame, + m_sync_max_voice_id); + + if (!RenderingInProgress()) + { + WARN_LOG(DSPHLE, "Trying to render audio while no rendering should be happening."); + return; } - m_mail_handler.PushMail(DSP_SYNC); - DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); - m_mail_handler.PushMail(0xF3550000 | sync); -} - -u32 ZeldaUCode::GetUpdateMs() -{ - return SConfig::GetInstance().bWii ? 3 : 5; -} - -void ZeldaUCode::DoState(PointerWrap &p) -{ - p.Do(m_sync_in_progress); - p.Do(m_max_voice); - p.Do(m_sync_flags); - - p.Do(m_num_sync_mail); - - p.Do(m_num_voices); - - p.Do(m_sync_cmd_pending); - p.Do(m_current_voice); - p.Do(m_current_buffer); - p.Do(m_num_buffers); - - p.Do(m_num_steps); - p.Do(m_list_in_progress); - p.Do(m_step); - p.Do(m_buffer); - - p.Do(m_read_offset); - - DoStateShared(p); + while (m_rendering_curr_frame < m_rendering_requested_frames) + { + while (m_rendering_curr_voice < m_rendering_voices_per_frame) + { + // If we are not meant to render this voice yet, go back to message + // processing. + if (m_rendering_curr_voice >= m_sync_max_voice_id) + return; + + // TODO(delroth): render. + + m_rendering_curr_voice++; + } + + SendCommandAck(CommandAck::STANDARD, 0xFF00 | m_rendering_curr_frame); + + m_rendering_curr_voice = 0; + m_sync_max_voice_id = 0; + m_rendering_curr_frame++; + } + + SendCommandAck(CommandAck::DONE_RENDERING, 0); + m_cmd_can_execute = false; // Block command execution until ACK is received. } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index c812f2b324..566b9a4915 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -19,35 +19,92 @@ public: void DoState(PointerWrap &p) override; +private: + // UCode state machine. The control flow in the Zelda UCode family is quite + // complex, using interrupt handlers heavily to handle incoming messages + // which, depending on the type, get handled immediately or are queued in a + // command buffer. In this implementation, the synchronous+interrupts flow + // of the original DSP implementation is rewritten in an asynchronous/coro + // + state machine style. It is less readable, but the best we can do given + // our constraints. + enum class MailState : u32 + { + WAITING, + RENDERING, + WRITING_CMD, + HALTED, + }; + MailState m_mail_current_state = MailState::WAITING; + u32 m_mail_expected_cmd_mails = 0; + + // Utility function to set the current state. Useful for debugging and + // logging as a hook point. + void SetMailState(MailState new_state) + { + // WARN_LOG(DSPHLE, "MailState %d -> %d", m_mail_current_state, new_state); + m_mail_current_state = new_state; + } + + // Voice synchronization / audio rendering flow control. When rendering an + // audio frame, only voices up to max_voice_id will be rendered until a + // sync mail arrives, increasing the value of max_voice_id. Additionally, + // these sync mails contain 16 bit values that are used for TODO. + u32 m_sync_max_voice_id = 0; + std::array m_sync_flags; + + // Command buffer (circular queue with r/w indices). Filled by HandleMail + // when the state machine is in WRITING_CMD state. Commands get executed + // when entering WAITING state and we are not rendering audio. + std::array m_cmd_buffer; + u32 m_read_offset = 0; + u32 m_write_offset = 0; + u32 m_pending_commands_count = 0; + bool m_cmd_can_execute = true; + + // Reads a 32 bit value from the command buffer. Advances the read pointer. u32 Read32() { - u32 res = *(u32*)&m_buffer[m_read_offset]; - m_read_offset += 4; + if (m_read_offset == m_write_offset) + { + ERROR_LOG(DSPHLE, "Reading too many command params"); + return 0; + } + + u32 res = m_cmd_buffer[m_read_offset]; + m_read_offset = (m_read_offset + 1) % (sizeof (m_cmd_buffer) / sizeof (u32)); return res; } -private: - bool m_sync_in_progress; - u32 m_max_voice; - u32 m_sync_flags[16]; + // Writes a 32 bit value to the command buffer. Advances the write pointer. + void Write32(u32 val) + { + m_cmd_buffer[m_write_offset] = val; + m_write_offset = (m_write_offset + 1) % (sizeof (m_cmd_buffer) / sizeof (u32)); + } - // Used by SMS version - u32 m_num_sync_mail; + // Tries to run as many commands as possible until either the command + // buffer is empty (pending_commands == 0) or we reached a long lived + // command that needs to hijack the mail control flow. + // + // Might change the current state to indicate crashy commands. + void RunPendingCommands(); - u32 m_num_voices; + // Sends the two mails from DSP to CPU to ack the command execution. + enum class CommandAck : u32 + { + STANDARD, + DONE_RENDERING, + }; + void SendCommandAck(CommandAck ack_type, u16 sync_value); - bool m_sync_cmd_pending; - u32 m_current_voice; - u32 m_current_buffer; - u32 m_num_buffers; + // Audio rendering state. + u32 m_rendering_requested_frames = 0; + u16 m_rendering_voices_per_frame = 0; + u32 m_rendering_mram_lbuf_addr = 0; + u32 m_rendering_mram_rbuf_addr = 0; + u32 m_rendering_curr_frame = 0; + u32 m_rendering_curr_voice = 0; - // List, buffer management ===================== - u32 m_num_steps; - bool m_list_in_progress; - u32 m_step; - u8 m_buffer[1024]; - - u32 m_read_offset; - - void ExecuteList(); + bool RenderingInProgress() const { return m_rendering_curr_frame != m_rendering_requested_frames; } + void RenderAudio(); }; From e35b83fabf4e48f1c1f1d3325948aed81af6f0ec Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 30 Nov 2014 02:18:01 +0100 Subject: [PATCH 07/59] ZeldaHLE: Implement more infrastructure and one sample source. Now renders some of the cutscene audio in Zelda TWW. Most of the work around sample sources is in a good enough state, even though it is still missing features like Dolby mixing, IIR, etc. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 558 +++++++++++++++++++- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 146 ++++- 2 files changed, 689 insertions(+), 15 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index a3607360eb..922acc1acf 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -53,7 +53,7 @@ void ZeldaUCode::DoState(PointerWrap &p) p.Do(m_mail_expected_cmd_mails); p.Do(m_sync_max_voice_id); - p.Do(m_sync_flags); + p.Do(m_sync_voice_skip_flags); p.Do(m_cmd_buffer); p.Do(m_read_offset); @@ -63,11 +63,11 @@ void ZeldaUCode::DoState(PointerWrap &p) p.Do(m_rendering_requested_frames); p.Do(m_rendering_voices_per_frame); - p.Do(m_rendering_mram_lbuf_addr); - p.Do(m_rendering_mram_rbuf_addr); p.Do(m_rendering_curr_frame); p.Do(m_rendering_curr_voice); + m_renderer.DoState(p); + DoStateShared(p); } @@ -139,7 +139,7 @@ void ZeldaUCode::HandleMail(u32 mail) case MailState::RENDERING: m_sync_max_voice_id = (((mail >> 16) & 0xF) + 1) << 4; - m_sync_flags[(mail >> 16) & 0xFF] = mail & 0xFFFF; + m_sync_voice_skip_flags[(mail >> 16) & 0xFF] = mail & 0xFFFF; RenderAudio(); SetMailState(MailState::WAITING); @@ -206,21 +206,40 @@ void ZeldaUCode::RunPendingCommands() SetMailState(MailState::HALTED); return; - // Command 01: TODO: find a name and implement. + // Command 01: Setup/initialization command. Provides the address to + // voice parameter blocks (VPBs) as well as some array of coefficients + // used for mixing. case 0x01: - WARN_LOG(DSPHLE, "CMD01: %08x %08x %08x %08x", - Read32(), Read32(), Read32(), Read32()); + { m_rendering_voices_per_frame = extra_data; + + m_renderer.SetVPBBaseAddress(Read32()); + + u16* data_ptr = (u16*)HLEMemory_Get_Pointer(Read32()); + + std::array resampling_coeffs; + for (size_t i = 0; i < 0x100; ++i) + resampling_coeffs[i] = Common::swap16(data_ptr[i]); + m_renderer.SetResamplingCoeffs(std::move(resampling_coeffs)); + + std::array sine_table; + for (size_t i = 0; i < 0x80; ++i) + sine_table[i] = Common::swap16(data_ptr[0x200 + i]); + m_renderer.SetSineTable(std::move(sine_table)); + SendCommandAck(CommandAck::STANDARD, sync); + Read32(); Read32(); break; + } // Command 02: starts audio processing. NOTE: this handler uses return, // not break. This is because it hijacks the mail control flow and // stops processing of further commands until audio processing is done. case 0x02: m_rendering_requested_frames = (cmd_mail >> 16) & 0xFF; - m_rendering_mram_lbuf_addr = Read32(); - m_rendering_mram_rbuf_addr = Read32(); + m_renderer.SetOutputVolume(cmd_mail & 0xFFFF); + m_renderer.SetOutputLeftBufferAddr(Read32()); + m_renderer.SetOutputRightBufferAddr(Read32()); m_rendering_curr_frame = 0; m_rendering_curr_voice = 0; @@ -259,10 +278,12 @@ void ZeldaUCode::SendCommandAck(CommandAck ack_type, u16 sync_value) void ZeldaUCode::RenderAudio() { +#if 0 WARN_LOG(DSPHLE, "RenderAudio() frame %d/%d voice %d/%d (sync to %d)", m_rendering_curr_frame, m_rendering_requested_frames, m_rendering_curr_voice, m_rendering_voices_per_frame, m_sync_max_voice_id); +#endif if (!RenderingInProgress()) { @@ -272,6 +293,9 @@ void ZeldaUCode::RenderAudio() while (m_rendering_curr_frame < m_rendering_requested_frames) { + if (m_rendering_curr_voice == 0) + m_renderer.PrepareFrame(); + while (m_rendering_curr_voice < m_rendering_voices_per_frame) { // If we are not meant to render this voice yet, go back to message @@ -279,13 +303,19 @@ void ZeldaUCode::RenderAudio() if (m_rendering_curr_voice >= m_sync_max_voice_id) return; - // TODO(delroth): render. + // Test the sync flag for this voice, skip it if not set. + u16 flags = m_sync_voice_skip_flags[m_rendering_curr_voice >> 4]; + u8 bit = 0xF - (m_rendering_curr_voice & 0xF); + if (flags & (1 << bit)) + m_renderer.AddVoice(m_rendering_curr_voice); m_rendering_curr_voice++; } SendCommandAck(CommandAck::STANDARD, 0xFF00 | m_rendering_curr_frame); + m_renderer.FinalizeFrame(); + m_rendering_curr_voice = 0; m_sync_max_voice_id = 0; m_rendering_curr_frame++; @@ -294,3 +324,511 @@ void ZeldaUCode::RenderAudio() SendCommandAck(CommandAck::DONE_RENDERING, 0); m_cmd_can_execute = false; // Block command execution until ACK is received. } + +// Utility to define 32 bit accessors/modifiers methods based on two 16 bit +// fields named _l and _h. +#define DEFINE_32BIT_ACCESSOR(field_name, name) \ + u32 Get##name() const { return (field_name##_h << 16) | field_name##_l; } \ + void Set##name(u32 v) \ + { \ + field_name##_h = v >> 16; \ + field_name##_l = v & 0xFFFF; \ + } + +#pragma pack(push, 1) +struct ZeldaAudioRenderer::VPB +{ + static constexpr u16 SIZE_IN_WORDS = 0xC0; + static constexpr u16 RW_SIZE_IN_WORDS = 0x80; + + // If zero, skip processing this voice. + u16 enabled; + + // If non zero, skip processing this voice. + u16 done; + + // In 4.12 format. 1.0 (0x1000) means 0x50 raw samples from RAM/accelerator + // will be "resampled" to 0x50 input samples. 2.0 (0x2000) means 2 raw + // samples for one input samples. 0.5 (0x800) means one raw sample for 2 + // input samples. + u16 resampling_ratio; + + u16 unk_03; + + // If non zero, reset some value in the VPB when processing it. + u16 reset_vpb; + + u16 unk_05; + + // If non zero, input samples to this VPB will be the fixed value from + // VPB[33] (constant_sample_value). This is used when a voice is being + // terminated in order to force silence. + u16 use_constant_sample; + + // Number of samples that should be saved in the VPB for processing during + // future frames. Should be at most TODO. + u16 samples_to_keep_count; + + // Channel mixing information. Each voice can be mixed to 6 different + // channels, with separate volume information. + // + // Used only if VPB[2C] (use_dolby_volume) is not set. Otherwise, the + // values from VPB[0x20:0x2C] are used to mix to all available channels. + struct Channel + { + // Can be treated as an ID, but in the real world this is actually the + // address in DRAM of a DSP buffer. The game passes that information to + // the DSP, which means the game must know the memory layout of the DSP + // UCode... that's terrible. + u16 id; + + s16 target_volume; + s16 current_volume; + + u16 unk; + }; + Channel channels[6]; + + u16 unk_20_28[0x8]; + + // When using Dolby voice mixing (see VPB[2C] use_dolby_volume), the X + // (left/right) and Y (front/back) coordinates of the sound. 0x00 is all + // right/back, 0x7F is all left/front. Format is 0XXXXXXX0YYYYYYY. + u16 dolby_voice_position; + u8 GetDolbyVoiceX() const { return (dolby_voice_position >> 8) & 0x7F; } + u8 GetDolbyVoiceY() const { return dolby_voice_position & 0x7F; } + + // How much reverbation to apply to the Dolby mixed voice. 0 is none, + // 0x7FFF is the maximum value. + s16 dolby_reverb_factor; + + // The volume for the 0x50 samples being mixed will ramp between current + // and target. After the ramping is done, the current value is updated (to + // match target, usually). + s16 dolby_volume_current; + s16 dolby_volume_target; + + // If non zero, use positional audio mixing. Instead of using the channels + // information, use the 4 Dolby related VPB fields defined above. + u16 use_dolby_volume; + + u16 unk_2D; + u16 unk_2E; + u16 unk_2F; + + // Fractional part of the current sample position, in 0.12 format (all + // decimal part, 0x0800 = 0.5). The 4 top bits are unused. + u16 current_pos_frac; + + u16 unk_31; + u16 unk_32; + + // Value used as the constant sample value if VPB[6] (use_constant_sample) + // is set. Reset to the last sample value after each round of resampling. + s16 constant_sample; + + // Current position in the voice. Not needed for accelerator based voice + // types since the accelerator exposes a streaming based interface, but DMA + // based voice types (PCM16_FROM_MRAM for example) require it to know where + // to seek in the MRAM buffer. + u16 current_position_h; + u16 current_position_l; + DEFINE_32BIT_ACCESSOR(current_position, CurrentPosition) + + // Number of samples that will be processed before the loop point of the + // voice is reached. Maintained by the UCode and used by the game to + // schedule some parameters updates. + u16 samples_before_loop; + + u16 unk_37; + u16 unk_38; + u16 unk_39; + + // Remaining number of samples to load before considering the voice + // rendering complete and setting the done flag. Note that this is an + // absolute value that does not take into account loops. If a loop of 100 + // samples is played 4 times, remaining_length will have decreased by 400. + u16 remaining_length_h; + u16 remaining_length_l; + DEFINE_32BIT_ACCESSOR(remaining_length, RemainingLength) + + // Stores the last 4 resampled input samples after each frame, so that they + // can be used for future linear interpolation. + u16 resample_buffer[4]; + + u16 unk[0x80 - 0x40]; + + enum SamplesSourceType + { + // Samples stored in MRAM at an arbitrary sample rate (resampling is + // applied, unlike PCM16_FROM_MRAM_RAW). + SRC_PCM16_FROM_MRAM = 33, + }; + u16 samples_source_type; + + u16 unk_81; + u16 unk_82; + u16 unk_83; + u16 unk_84; + + // If true, ramp down quickly to a volume of zero, and end the voice (by + // setting VPB[1] done) when it reaches zero. + u16 end_requested; + + u16 unk_86; + u16 unk_87; + + // Base address used to download samples data after the loop point of the + // voice has been reached. + u16 dma_loop_address_h; + u16 dma_loop_address_l; + DEFINE_32BIT_ACCESSOR(dma_loop_address, DMALoopAddress) + + // Offset (in number of raw samples) of the start of the loop area in the + // voice. Note: some sample sources only use the _h part of this. + u16 loop_start_position_h; + u16 loop_start_position_l; + DEFINE_32BIT_ACCESSOR(loop_start_position, LoopStartPosition) + + // Base address used to download samples data before the loop point of the + // voice has been reached. + u16 dma_base_address_h; + u16 dma_base_address_l; + DEFINE_32BIT_ACCESSOR(dma_base_address, DMABaseAddress) + + u16 padding[SIZE_IN_WORDS]; +}; +#pragma pack(pop) + +void ZeldaAudioRenderer::PrepareFrame() +{ + if (m_prepared) + return; + + m_buf_front_left.fill(0); + m_buf_front_right.fill(0); + + // TODO: Dolby/reverb mixing here. + + m_prepared = true; +} + +void ZeldaAudioRenderer::AddVoice(u16 voice_id) +{ + VPB vpb; + FetchVPB(voice_id, &vpb); + + if (!vpb.enabled || vpb.done) + return; + + MixingBuffer input_samples; + LoadInputSamples(&input_samples, &vpb); + + // TODO: In place effects. + + // TODO: IIR.filter. + + // TODO: Loop, etc. + + if (vpb.use_dolby_volume) + { + if (vpb.end_requested) + { + vpb.dolby_volume_target = vpb.dolby_volume_current / 2; + if (vpb.dolby_volume_target == 0) + vpb.done = true; + } + + // Each of these volumes is in 1.15 fixed format. + s16 right_volume = m_sine_table[vpb.GetDolbyVoiceX()]; + s16 back_volume = m_sine_table[vpb.GetDolbyVoiceY()]; + s16 left_volume = m_sine_table[vpb.GetDolbyVoiceX() ^ 0x7F]; + s16 front_volume = m_sine_table[vpb.GetDolbyVoiceY() ^ 0x7F]; + + // Compute volume for each quadrant. + s16 quadrant_volumes[4] = { + (s16)((left_volume * front_volume) >> 16), + (s16)((left_volume * back_volume) >> 16), + (s16)((right_volume * front_volume) >> 16), + (s16)((right_volume * back_volume) >> 16), + }; + + // Compute the volume delta for each sample to match the difference + // between current and target volume. + s16 delta = vpb.dolby_volume_target - vpb.dolby_volume_current; + s16 volume_deltas[4]; + for (size_t i = 0; i < 4; ++i) + volume_deltas[i] = ((u16)quadrant_volumes[i] * delta) >> 16; + + // Apply master volume to each quadrant. + for (size_t i = 0; i < 4; ++i) + quadrant_volumes[i] = (quadrant_volumes[i] * vpb.dolby_volume_current) >> 16; + + // Compute reverb volume and ramp deltas. + s16 reverb_volumes[4], reverb_volume_deltas[4]; + s16 reverb_volume_factor = (vpb.dolby_volume_current * vpb.dolby_reverb_factor) >> 15; + for (size_t i = 0; i < 4; ++i) + { + reverb_volumes[i] = (quadrant_volumes[i] * reverb_volume_factor) >> 15; + reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> 16; + } + + struct { + MixingBuffer* buffer; + s16 volume; + s16 volume_delta; + } buffers[8] = { + { &m_buf_front_left, quadrant_volumes[0], volume_deltas[0] }, + { &m_buf_back_left, quadrant_volumes[1], volume_deltas[1] }, + { &m_buf_front_right, quadrant_volumes[2], volume_deltas[2] }, + { &m_buf_back_right, quadrant_volumes[3], volume_deltas[3] }, + + { &m_buf_front_left_reverb, reverb_volumes[0], reverb_volume_deltas[0] }, + { &m_buf_back_left_reverb, reverb_volumes[1], reverb_volume_deltas[1] }, + { &m_buf_front_right_reverb, reverb_volumes[2], reverb_volume_deltas[2] }, + { &m_buf_back_right_reverb, reverb_volumes[3], reverb_volume_deltas[3] }, + }; + for (const auto& buffer : buffers) + { + AddBuffersWithVolumeRamp(buffer.buffer, input_samples, buffer.volume << 16, + (buffer.volume_delta << 16) / (s32)buffer.buffer->size()); + } + + vpb.dolby_volume_current = vpb.dolby_volume_target; + } + else + { + // TODO: Store input samples if requested by the VPB. + + if (vpb.end_requested) + { + bool all_mute = true; + for (auto& channel : vpb.channels) + { + channel.target_volume = channel.current_volume / 2; + all_mute &= (channel.target_volume == 0); + } + if (all_mute) + vpb.done = true; + } + + // Map buffer "IDs"/addresses to our emulated buffers. + std::map buffers = { + { 0x0D00, &m_buf_front_left }, + { 0x0D60, &m_buf_front_right }, + { 0x0F40, &m_buf_back_left }, + { 0x0CA0, &m_buf_back_right }, + { 0x0E80, &m_buf_front_left_reverb }, + { 0x0EE0, &m_buf_front_right_reverb }, + { 0x0C00, &m_buf_back_left_reverb }, + { 0x0C50, &m_buf_back_right_reverb }, + }; + + for (auto& channel : vpb.channels) + { + if (!channel.id) + continue; + + s16 volume_delta = channel.target_volume - channel.current_volume; + s32 volume_step = (volume_delta << 16) / (s32)input_samples.size(); // In 1.31 format. + + // TODO: The last value of each channel structure is used to + // determine whether a channel should be skipped or not. Not + // implemented yet. + + if (!channel.current_volume && !volume_step) + continue; + + MixingBuffer* dst_buffer = buffers[channel.id]; + if (!dst_buffer) + { + ERROR_LOG(DSPHLE, "Mixing to an unmapped buffer: %04x", channel.id); + continue; + } + + s32 new_volume = AddBuffersWithVolumeRamp( + dst_buffer, input_samples, channel.current_volume << 16, + volume_step); + channel.current_volume = new_volume >> 16; + } + } + + StoreVPB(voice_id, vpb); +} + +void ZeldaAudioRenderer::FinalizeFrame() +{ + // TODO: Dolby mixing. + + ApplyVolumeInPlace_4_12(&m_buf_front_left, m_output_volume); + ApplyVolumeInPlace_4_12(&m_buf_front_right, m_output_volume); + + u16* ram_left_buffer = (u16*)HLEMemory_Get_Pointer(m_output_lbuf_addr); + u16* ram_right_buffer = (u16*)HLEMemory_Get_Pointer(m_output_rbuf_addr); + for (size_t i = 0; i < m_buf_front_left.size(); ++i) + { + ram_left_buffer[i] = Common::swap16(m_buf_front_left[i]); + ram_right_buffer[i] = Common::swap16(m_buf_front_right[i]); + } + m_output_lbuf_addr += sizeof (u16) * m_buf_front_left.size(); + m_output_rbuf_addr += sizeof (u16) * m_buf_front_right.size(); + + // TODO: Some more Dolby mixing. + + m_prepared = false; +} + +void ZeldaAudioRenderer::FetchVPB(u16 voice_id, VPB* vpb) +{ + u16* vpb_words = (u16*)vpb; + u16* ram_vpbs = (u16*)HLEMemory_Get_Pointer(m_vpb_base_addr); + + size_t base_idx = voice_id * VPB::SIZE_IN_WORDS; + for (size_t i = 0; i < VPB::SIZE_IN_WORDS; ++i) + vpb_words[i] = Common::swap16(ram_vpbs[base_idx + i]); +} + +void ZeldaAudioRenderer::StoreVPB(u16 voice_id, const VPB& vpb) +{ + const u16* vpb_words = (const u16*)&vpb; + u16* ram_vpbs = (u16*)HLEMemory_Get_Pointer(m_vpb_base_addr); + + size_t base_idx = voice_id * VPB::SIZE_IN_WORDS; + + // Only the first 0x80 words are transferred back - the rest is read-only. + for (size_t i = 0; i < VPB::RW_SIZE_IN_WORDS; ++i) + ram_vpbs[base_idx + i] = Common::swap16(vpb_words[i]); +} + +void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) +{ + // Input data pre-resampling. Resampled into the mixing buffer parameter at + // the end of processing, if needed. + // + // Maximum of 0x500 samples here - see NeededRawSamplesCount to understand + // this practical limit (resampling_ratio = 0xFFFF -> 0x500 samples). Add a + // margin of 4 that is needed for samples source that do resampling. + std::array raw_input_samples; + for (size_t i = 0; i < 4; ++i) + raw_input_samples[i] = vpb->resample_buffer[i]; + + if (vpb->use_constant_sample) + { + buffer->fill(vpb->constant_sample); + return; + } + + switch (vpb->samples_source_type) + { + case VPB::SRC_PCM16_FROM_MRAM: + { + DownloadRawSamplesFromMRAM(raw_input_samples.data() + 4, vpb, + NeededRawSamplesCount(*vpb)); + Resample(vpb, raw_input_samples.data(), buffer); + break; + } + + default: + ERROR_LOG(DSPHLE, "Using an unknown/unimplemented sample source: %04x", vpb->samples_source_type); + buffer->fill(0); + return; + } +} + +u16 ZeldaAudioRenderer::NeededRawSamplesCount(const VPB& vpb) +{ + // Both of these are 4.12 fixed point, so shift by 12 to get the int part. + return (vpb.current_pos_frac + 0x50 * vpb.resampling_ratio) >> 12; +} + +void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst) +{ + // Both in 20.12 format. + u32 ratio = vpb->resampling_ratio; + u32 pos = vpb->current_pos_frac; + + // Check if we need to do some interpolation. If the resampling ratio is + // more than 4:1, it's not worth it. + if ((ratio >> 12) >= 4) + { + for (s16& dst_sample : *dst) + { + pos += ratio; + dst_sample = src[pos >> 12]; + } + } + else + { + for (auto& dst_sample : *dst) + { + // We have 0x40 * 4 coeffs that need to be selected based on the + // most significant bits of the fractional part of the position. 12 + // bits >> 6 = 6 bits = 0x40. Multiply by 4 since there are 4 + // consecutive coeffs. + u32 coeffs_idx = ((pos & 0xFFF) >> 6) * 4; + const s16* coeffs = &m_resampling_coeffs[coeffs_idx]; + const s16* input = &src[pos >> 12]; + + s64 dst_sample_unclamped = 0; + for (size_t i = 0; i < 4; ++i) + dst_sample_unclamped += (s64)2 * coeffs[i] * input[i]; + dst_sample_unclamped >>= 16; + MathUtil::Clamp(&dst_sample_unclamped, (s64)-0x8000, (s64)0x7fff); + dst_sample = dst_sample_unclamped; + + pos += ratio; + } + } + + for (u32 i = 0; i < 4; ++i) + vpb->resample_buffer[i] = src[(pos >> 12) + i]; + vpb->constant_sample = (*dst)[dst->size() - 1]; + vpb->current_pos_frac = pos & 0xFFF; +} + +void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( + s16* dst, VPB* vpb, u16 requested_samples_count) +{ + u32 addr = vpb->GetDMABaseAddress() + vpb->current_position_h * sizeof (u16); + s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr); + + if (requested_samples_count > vpb->GetRemainingLength()) + { + s16 last_sample = 0; + for (u16 i = 0; i < vpb->GetRemainingLength(); ++i) + *dst++ = last_sample = Common::swap16(*src_ptr++); + for (u16 i = vpb->GetRemainingLength(); i < requested_samples_count; ++i) + *dst++ = last_sample; + + vpb->current_position_h += vpb->GetRemainingLength(); + vpb->SetRemainingLength(0); + vpb->done = true; + } + else + { + vpb->SetRemainingLength(vpb->GetRemainingLength() - requested_samples_count); + vpb->samples_before_loop = vpb->loop_start_position_h - vpb->current_position_h; + if (requested_samples_count <= vpb->samples_before_loop) + { + for (u16 i = 0; i < requested_samples_count; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->current_position_h += requested_samples_count; + } + else + { + for (u16 i = 0; i < vpb->samples_before_loop; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->SetDMABaseAddress(vpb->GetDMALoopAddress()); + src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetDMALoopAddress()); + for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->current_position_h = requested_samples_count - vpb->samples_before_loop; + } + } +} + +void ZeldaAudioRenderer::DoState(PointerWrap& p) +{ + p.Do(m_output_lbuf_addr); + p.Do(m_output_rbuf_addr); +} diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 566b9a4915..b94b89ec2a 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -5,8 +5,142 @@ #pragma once #include "Common/CommonTypes.h" +#include "Common/MathUtil.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" +class ZeldaAudioRenderer +{ +public: + void PrepareFrame(); + void AddVoice(u16 voice_id); + void FinalizeFrame(); + + void SetSineTable(std::array&& sine_table) { m_sine_table = sine_table; } + void SetResamplingCoeffs(std::array&& coeffs) { m_resampling_coeffs = coeffs; } + void SetVPBBaseAddress(u32 addr) { m_vpb_base_addr = addr; } + void SetOutputVolume(u16 volume) { m_output_volume = volume; } + void SetOutputLeftBufferAddr(u32 addr) { m_output_lbuf_addr = addr; } + void SetOutputRightBufferAddr(u32 addr) { m_output_rbuf_addr = addr; } + + void DoState(PointerWrap& p); + +private: + struct VPB; + + // Utility functions for audio operations. + + // Apply volume to a buffer. The volume is a fixed point integer, usually + // 1.15 or 4.12 in the DAC UCode. + template + void ApplyVolumeInPlace(std::array* buf, u16 vol) + { + for (size_t i = 0; i < N; ++i) + { + s32 tmp = (u32)(*buf)[i] * (u32)vol; + tmp >>= 16 - B; + MathUtil::Clamp(&tmp, -0x8000, 0x7fff); + (*buf)[i] = (s16)tmp; + } + } + template + void ApplyVolumeInPlace_1_15(std::array* buf, u16 vol) + { + ApplyVolumeInPlace(buf, vol); + } + template + void ApplyVolumeInPlace_4_12(std::array* buf, u16 vol) + { + ApplyVolumeInPlace(buf, vol); + } + + // Mixes two buffers together while applying a volume to one of them. The + // volume ramps up/down in N steps using the provided step delta value. + // + // Note: On a real GC, the stepping happens in 32 steps instead. But hey, + // we can do better here with very low risk. Why not? :) + template + s32 AddBuffersWithVolumeRamp(std::array* dst, + const std::array& src, + s32 vol, s32 step) + { + if (!vol && !step) + return vol; + + for (size_t i = 0; i < N; ++i) + { + (*dst)[i] += ((vol >> 16) * src[i]) >> 16; + vol += step; + } + + return vol; + } + + // Whether the frame needs to be prepared or not. + bool m_prepared = false; + + // MRAM addresses where output samples should be copied. + u32 m_output_lbuf_addr = 0; + u32 m_output_rbuf_addr = 0; + + // Output volume applied to buffers before being uploaded to RAM. + u16 m_output_volume = 0; + + // Mixing buffers. + typedef std::array MixingBuffer; + MixingBuffer m_buf_front_left; + MixingBuffer m_buf_front_right; + MixingBuffer m_buf_back_left; + MixingBuffer m_buf_back_right; + MixingBuffer m_buf_front_left_reverb; + MixingBuffer m_buf_front_right_reverb; + MixingBuffer m_buf_back_left_reverb; + MixingBuffer m_buf_back_right_reverb; + + // Base address where VPBs are stored linearly in RAM. + u32 m_vpb_base_addr; + void FetchVPB(u16 voice_id, VPB* vpb); + void StoreVPB(u16 voice_id, const VPB& vpb); + + // Sine table transferred from MRAM. Contains sin(x) values for x in + // [0.0;pi/4] (sin(x) in [1.0;0.0]), in 1.15 fixed format. + std::array m_sine_table; + + // Fills up a buffer with the input samples for a voice, represented by its + // VPB. + void LoadInputSamples(MixingBuffer* buffer, VPB* vpb); + + // Raw samples (pre-resampling) that need to be generated to result in 0x50 + // post-resampling input samples. + u16 NeededRawSamplesCount(const VPB& vpb); + + // Resamples raw samples to 0x50 input samples, using the resampling ratio + // and current position information from the VPB. + void Resample(VPB* vpb, const s16* src, MixingBuffer* dst); + + // Coefficients used for resampling. + std::array m_resampling_coeffs{}; + + // If non zero, base MRAM address for sound data transfers from ARAM. On + // the Wii, this points to some MRAM location since there is no ARAM to be + // used. If zero, use the top of ARAM. + u32 m_aram_base_addr = 0; + void* GetARAMPtr() const; + + // Downloads PCM encoded samples from ARAM. Handles looping and other + // parameters appropriately. + template void DownloadPCMSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); + + // Downloads AFC encoded samples from ARAM and decode them. Handles looping + // and other parameters appropriately. + void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); + void DecodeAFC(VPB* vpb, s16* dst, size_t block_count); + std::array m_afc_coeffs{}; + + // Downloads samples from MRAM while handling appropriate length / looping + // behavior. + void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); +}; + class ZeldaUCode : public UCodeInterface { public: @@ -48,9 +182,10 @@ private: // Voice synchronization / audio rendering flow control. When rendering an // audio frame, only voices up to max_voice_id will be rendered until a // sync mail arrives, increasing the value of max_voice_id. Additionally, - // these sync mails contain 16 bit values that are used for TODO. + // these sync mails contain 16 bit values that are used as bitfields to + // control voice skipping on a voice per voice level. u32 m_sync_max_voice_id = 0; - std::array m_sync_flags; + std::array m_sync_voice_skip_flags; // Command buffer (circular queue with r/w indices). Filled by HandleMail // when the state machine is in WRITING_CMD state. Commands get executed @@ -97,14 +232,15 @@ private: }; void SendCommandAck(CommandAck ack_type, u16 sync_value); - // Audio rendering state. + // Audio rendering flow control state. u32 m_rendering_requested_frames = 0; u16 m_rendering_voices_per_frame = 0; - u32 m_rendering_mram_lbuf_addr = 0; - u32 m_rendering_mram_rbuf_addr = 0; u32 m_rendering_curr_frame = 0; u32 m_rendering_curr_voice = 0; bool RenderingInProgress() const { return m_rendering_curr_frame != m_rendering_requested_frames; } void RenderAudio(); + + // Main object handling audio rendering logic and state. + ZeldaAudioRenderer m_renderer; }; From b62f1ed45389ef50909de72e4a02333fdf736687 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 30 Nov 2014 02:22:28 +0100 Subject: [PATCH 08/59] ZeldaHLE: fix windows build --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 922acc1acf..2b8f4e37bd 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -338,8 +338,8 @@ void ZeldaUCode::RenderAudio() #pragma pack(push, 1) struct ZeldaAudioRenderer::VPB { - static constexpr u16 SIZE_IN_WORDS = 0xC0; - static constexpr u16 RW_SIZE_IN_WORDS = 0x80; + static const u16 SIZE_IN_WORDS = 0xC0; + static const u16 RW_SIZE_IN_WORDS = 0x80; // If zero, skip processing this voice. u16 enabled; @@ -670,8 +670,8 @@ void ZeldaAudioRenderer::FinalizeFrame() ram_left_buffer[i] = Common::swap16(m_buf_front_left[i]); ram_right_buffer[i] = Common::swap16(m_buf_front_right[i]); } - m_output_lbuf_addr += sizeof (u16) * m_buf_front_left.size(); - m_output_rbuf_addr += sizeof (u16) * m_buf_front_right.size(); + m_output_lbuf_addr += sizeof (u16) * (u32)m_buf_front_left.size(); + m_output_rbuf_addr += sizeof (u16) * (u32)m_buf_front_right.size(); // TODO: Some more Dolby mixing. From 33c29d8aab080d619edcbd264b3bb38c61e56dd1 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 30 Nov 2014 02:23:55 +0100 Subject: [PATCH 09/59] ZeldaHLE: fix windows build again --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 2b8f4e37bd..aacd1368ec 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -774,7 +774,7 @@ void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst) dst_sample_unclamped += (s64)2 * coeffs[i] * input[i]; dst_sample_unclamped >>= 16; MathUtil::Clamp(&dst_sample_unclamped, (s64)-0x8000, (s64)0x7fff); - dst_sample = dst_sample_unclamped; + dst_sample = (s16)dst_sample_unclamped; pos += ratio; } From 580fe12c5b373d9cb410e98ee109aa6e5f4ca72e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 13 Dec 2014 16:56:50 +0100 Subject: [PATCH 10/59] Zelda HLE: Implement HQ AFC samples decoding. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 252 +++++++++++++++++--- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 7 + 2 files changed, 232 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index aacd1368ec..d6227e425b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -227,8 +227,14 @@ void ZeldaUCode::RunPendingCommands() sine_table[i] = Common::swap16(data_ptr[0x200 + i]); m_renderer.SetSineTable(std::move(sine_table)); + u16* afc_coeffs_ptr = (u16*)HLEMemory_Get_Pointer(Read32()); + std::array afc_coeffs; + for (size_t i = 0; i < 0x20; ++i) + afc_coeffs[i] = Common::swap16(afc_coeffs_ptr[i]); + m_renderer.SetAfcCoeffs(std::move(afc_coeffs)); + SendCommandAck(CommandAck::STANDARD, sync); - Read32(); Read32(); + Read32(); break; } @@ -278,13 +284,6 @@ void ZeldaUCode::SendCommandAck(CommandAck ack_type, u16 sync_value) void ZeldaUCode::RenderAudio() { -#if 0 - WARN_LOG(DSPHLE, "RenderAudio() frame %d/%d voice %d/%d (sync to %d)", - m_rendering_curr_frame, m_rendering_requested_frames, - m_rendering_curr_voice, m_rendering_voices_per_frame, - m_sync_max_voice_id); -#endif - if (!RenderingInProgress()) { WARN_LOG(DSPHLE, "Trying to render audio while no rendering should be happening."); @@ -421,7 +420,10 @@ struct ZeldaAudioRenderer::VPB u16 current_pos_frac; u16 unk_31; - u16 unk_32; + + // Number of remaining decoded AFC samples in the VPB internal buffer (see + // VPB[0x58]) that haven't been output yet. + u16 afc_remaining_decoded_samples; // Value used as the constant sample value if VPB[6] (use_constant_sample) // is set. Reset to the last sample value after each round of resampling. @@ -441,8 +443,11 @@ struct ZeldaAudioRenderer::VPB u16 samples_before_loop; u16 unk_37; - u16 unk_38; - u16 unk_39; + + // Current address used to stream samples for the ARAM sample source types. + u16 current_aram_addr_h; + u16 current_aram_addr_l; + DEFINE_32BIT_ACCESSOR(current_aram_addr, CurrentARAMAddr) // Remaining number of samples to load before considering the voice // rendering complete and setting the done flag. Note that this is an @@ -454,21 +459,40 @@ struct ZeldaAudioRenderer::VPB // Stores the last 4 resampled input samples after each frame, so that they // can be used for future linear interpolation. - u16 resample_buffer[4]; + s16 resample_buffer[4]; - u16 unk[0x80 - 0x40]; + // TODO: document and implement. + s16 prev_input_samples[0x18]; + + // Values from the last decoded AFC block. The last two values are + // especially important since AFC decoding - as a variant of ADPCM - + // requires the two latest sample values to be able to decode future + // samples. + s16 afc_remaining_samples[0x10]; + s16* AFCYN2() { return &afc_remaining_samples[0xE]; } + s16* AFCYN1() { return &afc_remaining_samples[0xF]; } + + u16 unk_68_80[0x80 - 0x68]; enum SamplesSourceType { + // Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded, + // at an arbitrary sample rate (resampling is applied). + SRC_AFC_HQ_FROM_ARAM = 9, // Samples stored in MRAM at an arbitrary sample rate (resampling is // applied, unlike PCM16_FROM_MRAM_RAW). SRC_PCM16_FROM_MRAM = 33, }; u16 samples_source_type; - u16 unk_81; - u16 unk_82; - u16 unk_83; + // If non zero, indicates that the sound should loop. + u16 is_looping; + + // For AFC looping voices, the values of the last 2 samples before the + // start of the loop, in order to be able to decode samples after looping. + s16 loop_yn1; + s16 loop_yn2; + u16 unk_84; // If true, ramp down quickly to a volume of zero, and end the voice (by @@ -480,21 +504,23 @@ struct ZeldaAudioRenderer::VPB // Base address used to download samples data after the loop point of the // voice has been reached. - u16 dma_loop_address_h; - u16 dma_loop_address_l; - DEFINE_32BIT_ACCESSOR(dma_loop_address, DMALoopAddress) + u16 loop_address_h; + u16 loop_address_l; + DEFINE_32BIT_ACCESSOR(loop_address, LoopAddress) // Offset (in number of raw samples) of the start of the loop area in the // voice. Note: some sample sources only use the _h part of this. + // + // TODO: rename to length? confusion with remaining_length... u16 loop_start_position_h; u16 loop_start_position_l; DEFINE_32BIT_ACCESSOR(loop_start_position, LoopStartPosition) // Base address used to download samples data before the loop point of the // voice has been reached. - u16 dma_base_address_h; - u16 dma_base_address_l; - DEFINE_32BIT_ACCESSOR(dma_base_address, DMABaseAddress) + u16 base_address_h; + u16 base_address_l; + DEFINE_32BIT_ACCESSOR(base_address, BaseAddress) u16 padding[SIZE_IN_WORDS]; }; @@ -653,6 +679,11 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) } } + // By then the VPB has been reset, unless we're in the "constant sample" / + // silence mode. + if (!vpb.use_constant_sample) + vpb.reset_vpb = false; + StoreVPB(voice_id, vpb); } @@ -720,13 +751,17 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) switch (vpb->samples_source_type) { + case VPB::SRC_AFC_HQ_FROM_ARAM: + DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, + NeededRawSamplesCount(*vpb)); + Resample(vpb, raw_input_samples.data(), buffer); + break; + case VPB::SRC_PCM16_FROM_MRAM: - { DownloadRawSamplesFromMRAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); Resample(vpb, raw_input_samples.data(), buffer); break; - } default: ERROR_LOG(DSPHLE, "Using an unknown/unimplemented sample source: %04x", vpb->samples_source_type); @@ -789,7 +824,7 @@ void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst) void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( s16* dst, VPB* vpb, u16 requested_samples_count) { - u32 addr = vpb->GetDMABaseAddress() + vpb->current_position_h * sizeof (u16); + u32 addr = vpb->GetBaseAddress() + vpb->current_position_h * sizeof (u16); s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr); if (requested_samples_count > vpb->GetRemainingLength()) @@ -818,8 +853,8 @@ void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( { for (u16 i = 0; i < vpb->samples_before_loop; ++i) *dst++ = Common::swap16(*src_ptr++); - vpb->SetDMABaseAddress(vpb->GetDMALoopAddress()); - src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetDMALoopAddress()); + vpb->SetBaseAddress(vpb->GetLoopAddress()); + src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetLoopAddress()); for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i) *dst++ = Common::swap16(*src_ptr++); vpb->current_position_h = requested_samples_count - vpb->samples_before_loop; @@ -827,6 +862,169 @@ void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( } } +void ZeldaAudioRenderer::DownloadAFCSamplesFromARAM( + s16* dst, VPB* vpb, u16 requested_samples_count) +{ + if (vpb->reset_vpb) + { + *vpb->AFCYN1() = 0; + *vpb->AFCYN2() = 0; + vpb->afc_remaining_decoded_samples = 0; + vpb->SetRemainingLength(vpb->GetLoopStartPosition()); + vpb->SetCurrentARAMAddr(vpb->GetBaseAddress()); + } + + if (vpb->done) + { + for (u16 i = 0; i < requested_samples_count; ++i) + dst[i] = 0; + return; + } + + // Try several things until we have output enough samples. + while (true) + { + // Try to push currently cached/already decoded samples. + u16 remaining_to_output = std::min(vpb->afc_remaining_decoded_samples, + requested_samples_count); + for (size_t i = 0x10 - remaining_to_output; i < 0x10; ++i) + *dst++ = vpb->afc_remaining_samples[i]; + + vpb->afc_remaining_decoded_samples -= remaining_to_output; + requested_samples_count -= remaining_to_output; + + if (requested_samples_count == 0) + { + return; // We have output everything we needed. + } + else if (requested_samples_count <= vpb->GetRemainingLength()) + { + // Each AFC block is 16 samples. + u16 requested_blocks_count = (requested_samples_count + 0xF) >> 4; + u16 decoded_samples_count = requested_blocks_count << 4; + + if (decoded_samples_count < vpb->GetRemainingLength()) + { + vpb->afc_remaining_decoded_samples = + decoded_samples_count - requested_samples_count; + vpb->SetRemainingLength(vpb->GetRemainingLength() - decoded_samples_count); + } + else + { + vpb->afc_remaining_decoded_samples = + vpb->GetRemainingLength() - requested_samples_count; + vpb->SetRemainingLength(0); + } + + DecodeAFC(vpb, dst, requested_blocks_count); + + for (size_t i = 0; i < 0x10; ++i) + vpb->afc_remaining_samples[i] = dst[decoded_samples_count - 0x10 + i]; + + return; + } + else + { + // More samples asked than available. Either complete the sound, or + // start looping. + if (vpb->GetRemainingLength()) // Skip if we cannot load anything. + { + requested_samples_count -= vpb->GetRemainingLength(); + u16 requested_blocks_count = (vpb->GetRemainingLength() + 0xF) >> 4; + DecodeAFC(vpb, dst, requested_blocks_count); + dst += vpb->GetRemainingLength(); + } + + if (!vpb->is_looping) + { + vpb->done = true; + for (size_t i = 0; i < requested_samples_count; ++i) + *dst++ = 0; + return; + } + else + { + // We need to loop. Compute the new position, decode a block, + // and loop back to the beginning of the download logic. + + // Use the fact that the sample source number also represents + // the number of bytes per 16 samples. + u32 loop_off_in_bytes = + (vpb->GetLoopAddress() >> 4) * vpb->samples_source_type; + u32 loop_start_addr = vpb->GetBaseAddress() + loop_off_in_bytes; + vpb->SetCurrentARAMAddr(loop_start_addr); + + *vpb->AFCYN2() = vpb->loop_yn2; + *vpb->AFCYN1() = vpb->loop_yn1; + + DecodeAFC(vpb, vpb->afc_remaining_samples, 1); + + // Realign and recompute the number of internally cached + // samples and the current position. + vpb->afc_remaining_decoded_samples = + 0x10 - (vpb->GetLoopAddress() & 0xF); + + u32 remaining_length = vpb->GetLoopStartPosition(); + remaining_length -= vpb->afc_remaining_decoded_samples; + remaining_length -= vpb->GetLoopAddress(); + vpb->SetRemainingLength(remaining_length); + continue; + } + } + } +} + +void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) +{ + u32 addr = vpb->GetCurrentARAMAddr(); + u8* src = (u8*)DSP::GetARAMPtr() + addr; + vpb->SetCurrentARAMAddr(addr + block_count * vpb->samples_source_type); + + for (size_t b = 0; b < block_count; ++b) + { + s16 nibbles[16]; + s16 delta = 1 << ((*src >> 4) & 0xF); + s16 idx = (*src & 0xF); + src++; + + if (vpb->samples_source_type == VPB::SRC_AFC_HQ_FROM_ARAM) + { + for (size_t i = 0; i < 16; i += 2) + { + nibbles[i + 0] = *src >> 4; + nibbles[i + 1] = *src & 0xF; + src++; + } + for (auto& nibble : nibbles) + { + if (nibble >= 8) + nibble = nibble - 16; + nibble <<= 11; + } + } + else + { + // TODO: LQ samples. + } + + s32 yn1 = *vpb->AFCYN1(), yn2 = *vpb->AFCYN2(); + for (size_t i = 0; i < 16; ++i) + { + s32 sample = delta * nibbles[i] + + yn1 * m_afc_coeffs[idx * 2] + + yn2 * m_afc_coeffs[idx * 2 + 1]; + sample >>= 11; + MathUtil::Clamp(&sample, -0x8000, 0x7fff); + *dst++ = (s16)sample; + yn2 = yn1; + yn1 = sample; + } + + *vpb->AFCYN2() = yn2; + *vpb->AFCYN1() = yn1; + } +} + void ZeldaAudioRenderer::DoState(PointerWrap& p) { p.Do(m_output_lbuf_addr); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index b94b89ec2a..1b5b32057b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -17,6 +17,7 @@ public: void SetSineTable(std::array&& sine_table) { m_sine_table = sine_table; } void SetResamplingCoeffs(std::array&& coeffs) { m_resampling_coeffs = coeffs; } + void SetAfcCoeffs(std::array&& coeffs) { m_afc_coeffs = coeffs; } void SetVPBBaseAddress(u32 addr) { m_vpb_base_addr = addr; } void SetOutputVolume(u16 volume) { m_output_volume = volume; } void SetOutputLeftBufferAddr(u32 addr) { m_output_lbuf_addr = addr; } @@ -139,6 +140,12 @@ private: // Downloads samples from MRAM while handling appropriate length / looping // behavior. void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); + + // Download AFC encoded samples from ARAM and decode them. Handles looping + // and other parameters appropriately. + void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); + void DecodeAFC(VPB* vpb, s16* dst, size_t block_count); + std::array m_afc_coeffs; }; class ZeldaUCode : public UCodeInterface From 2bb3daf0279ee1443cc1c55363baa6d55a1547f6 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 13 Dec 2014 17:15:31 +0100 Subject: [PATCH 11/59] Zelda HLE: Implement square wave sample source. Also fix Windows build. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index d6227e425b..f11f1c9a1b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -476,6 +476,8 @@ struct ZeldaAudioRenderer::VPB enum SamplesSourceType { + // Simple square wave at 50% amplitude and 16KHz. + SRC_SQUARE_WAVE = 0, // Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded, // at an arbitrary sample rate (resampling is applied). SRC_AFC_HQ_FROM_ARAM = 9, @@ -751,6 +753,11 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) switch (vpb->samples_source_type) { + case VPB::SRC_SQUARE_WAVE: + for (size_t i = 0; i < buffer->size(); ++i) + (*buffer)[i] = (i & 1) ? 0x4000 : 0xC000; + break; + case VPB::SRC_AFC_HQ_FROM_ARAM: DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); @@ -978,7 +985,7 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) { u32 addr = vpb->GetCurrentARAMAddr(); u8* src = (u8*)DSP::GetARAMPtr() + addr; - vpb->SetCurrentARAMAddr(addr + block_count * vpb->samples_source_type); + vpb->SetCurrentARAMAddr(addr + (u32)block_count * vpb->samples_source_type); for (size_t b = 0; b < block_count; ++b) { From 63712e58fd82d3a607af7433279b2d66619b0b4c Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 20 Dec 2014 03:41:23 +0100 Subject: [PATCH 12/59] Zelda HLE: Convert some ERROR_LOG to PanicAlert. While these are not really unrecoverable errors, while Zelda HLE is in a testing / development phase it is useful to notice these "unexpected" cases (or expected without known ways to reproduce) by making them as hard as possible to ignore. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index f11f1c9a1b..39b679e00d 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -86,8 +86,8 @@ void ZeldaUCode::HandleMail(u32 mail) { if ((mail >> 16) != 0xCDD1) { - ERROR_LOG(DSPHLE, "Rendering end mail without prefix CDD1: %08x", - mail); + PanicAlert("Rendering end mail without prefix CDD1: %08x", + mail); } switch (mail & 0xFFFF) @@ -536,6 +536,15 @@ void ZeldaAudioRenderer::PrepareFrame() m_buf_front_left.fill(0); m_buf_front_right.fill(0); + ApplyVolumeInPlace_1_15(&m_buf_back_left, 0x6784); + ApplyVolumeInPlace_1_15(&m_buf_back_right, 0x6784); + + // TODO: Back left and back right should have a filter applied to them, + // then get mixed into front left and front right. In practice, TWW never + // uses this AFAICT. PanicAlert to help me find places that use this. + if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0) + PanicAlert("Zelda HLE using back mixing buffers"); + // TODO: Dolby/reverb mixing here. m_prepared = true; @@ -554,9 +563,7 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) // TODO: In place effects. - // TODO: IIR.filter. - - // TODO: Loop, etc. + // TODO: IIR filter. if (vpb.use_dolby_volume) { @@ -771,7 +778,7 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) break; default: - ERROR_LOG(DSPHLE, "Using an unknown/unimplemented sample source: %04x", vpb->samples_source_type); + PanicAlert("Using an unknown/unimplemented sample source: %04x", vpb->samples_source_type); buffer->fill(0); return; } From 91fade5e8940fe0cd5ad7d0707a9d557bb33d37c Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 20 Dec 2014 03:42:40 +0100 Subject: [PATCH 13/59] Zelda HLE: Properly implement the square wave generation. Zelda TWW magic meter works properly now. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 39b679e00d..a679f55149 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -761,9 +761,16 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) switch (vpb->samples_source_type) { case VPB::SRC_SQUARE_WAVE: + { + u32 pos = vpb->current_pos_frac << 1; for (size_t i = 0; i < buffer->size(); ++i) - (*buffer)[i] = (i & 1) ? 0x4000 : 0xC000; + { + (*buffer)[i] = ((pos >> 16) & 1) ? 0x4000 : 0xC000; + pos += vpb->resampling_ratio; + } + vpb->current_pos_frac = (pos >> 1) & 0xFFFF; break; + } case VPB::SRC_AFC_HQ_FROM_ARAM: DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, From 34341af17d8cd46812f07d4b82044ff07fd3225c Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 20 Dec 2014 04:11:10 +0100 Subject: [PATCH 14/59] Zelda HLE: Implement saw wave generation (sample source 0001). Fixes issue 7961. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index a679f55149..5f99618a7d 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -476,8 +476,12 @@ struct ZeldaAudioRenderer::VPB enum SamplesSourceType { - // Simple square wave at 50% amplitude and 16KHz. + // Simple square wave at 50% amplitude and frequency controlled via the + // resampling ratio. SRC_SQUARE_WAVE = 0, + // Simple saw wave at 100% amplitude and frequency controlled via the + // resampling ratio. + SRC_SAW_WAVE = 1, // Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded, // at an arbitrary sample rate (resampling is applied). SRC_AFC_HQ_FROM_ARAM = 9, @@ -772,6 +776,18 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) break; } + case VPB::SRC_SAW_WAVE: + { + u32 pos = vpb->current_pos_frac; + for (size_t i = 0; i < buffer->size(); ++i) + { + (*buffer)[i] = pos & 0xFFFF; + pos += (vpb->resampling_ratio) >> 1; + } + vpb->current_pos_frac = pos & 0xFFFF; + break; + } + case VPB::SRC_AFC_HQ_FROM_ARAM: DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); From 58fd39d57a0fd91603c20e665f42a1268ba5788a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 20 Dec 2014 04:24:43 +0100 Subject: [PATCH 15/59] Zelda HLE: Value-initialize the std::arrays. I was under the wrong impression that std::array's default constructor performed value initialization. Turns out it does not, so an array of POD will not be initialized. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 ++ Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 40 +++++++-------------- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 5f99618a7d..18424e573c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -549,6 +549,9 @@ void ZeldaAudioRenderer::PrepareFrame() if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0) PanicAlert("Zelda HLE using back mixing buffers"); + m_buf_back_left.fill(0); + m_buf_back_right.fill(0); + // TODO: Dolby/reverb mixing here. m_prepared = true; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 1b5b32057b..7dbfadbf82 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -88,14 +88,14 @@ private: // Mixing buffers. typedef std::array MixingBuffer; - MixingBuffer m_buf_front_left; - MixingBuffer m_buf_front_right; - MixingBuffer m_buf_back_left; - MixingBuffer m_buf_back_right; - MixingBuffer m_buf_front_left_reverb; - MixingBuffer m_buf_front_right_reverb; - MixingBuffer m_buf_back_left_reverb; - MixingBuffer m_buf_back_right_reverb; + MixingBuffer m_buf_front_left{}; + MixingBuffer m_buf_front_right{}; + MixingBuffer m_buf_back_left{}; + MixingBuffer m_buf_back_right{}; + MixingBuffer m_buf_front_left_reverb{}; + MixingBuffer m_buf_front_right_reverb{}; + MixingBuffer m_buf_back_left_reverb{}; + MixingBuffer m_buf_back_right_reverb{}; // Base address where VPBs are stored linearly in RAM. u32 m_vpb_base_addr; @@ -104,7 +104,7 @@ private: // Sine table transferred from MRAM. Contains sin(x) values for x in // [0.0;pi/4] (sin(x) in [1.0;0.0]), in 1.15 fixed format. - std::array m_sine_table; + std::array m_sine_table{}; // Fills up a buffer with the input samples for a voice, represented by its // VPB. @@ -121,22 +121,6 @@ private: // Coefficients used for resampling. std::array m_resampling_coeffs{}; - // If non zero, base MRAM address for sound data transfers from ARAM. On - // the Wii, this points to some MRAM location since there is no ARAM to be - // used. If zero, use the top of ARAM. - u32 m_aram_base_addr = 0; - void* GetARAMPtr() const; - - // Downloads PCM encoded samples from ARAM. Handles looping and other - // parameters appropriately. - template void DownloadPCMSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); - - // Downloads AFC encoded samples from ARAM and decode them. Handles looping - // and other parameters appropriately. - void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); - void DecodeAFC(VPB* vpb, s16* dst, size_t block_count); - std::array m_afc_coeffs{}; - // Downloads samples from MRAM while handling appropriate length / looping // behavior. void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); @@ -145,7 +129,7 @@ private: // and other parameters appropriately. void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); void DecodeAFC(VPB* vpb, s16* dst, size_t block_count); - std::array m_afc_coeffs; + std::array m_afc_coeffs{}; }; class ZeldaUCode : public UCodeInterface @@ -192,12 +176,12 @@ private: // these sync mails contain 16 bit values that are used as bitfields to // control voice skipping on a voice per voice level. u32 m_sync_max_voice_id = 0; - std::array m_sync_voice_skip_flags; + std::array m_sync_voice_skip_flags{}; // Command buffer (circular queue with r/w indices). Filled by HandleMail // when the state machine is in WRITING_CMD state. Commands get executed // when entering WAITING state and we are not rendering audio. - std::array m_cmd_buffer; + std::array m_cmd_buffer{}; u32 m_read_offset = 0; u32 m_write_offset = 0; u32 m_pending_commands_count = 0; From c58aece9ad1d66369e759d42afd568f046f4d42e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 20 Dec 2014 14:59:21 +0100 Subject: [PATCH 16/59] Zelda HLE: Implement the PCM8 sample source. Reorder some things in the source code to match the sample source definition order. Add and remove some TODOs. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 128 ++++++++++++++------ Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 12 +- 2 files changed, 101 insertions(+), 39 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 18424e573c..8a6d1b216c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -357,7 +357,9 @@ struct ZeldaAudioRenderer::VPB // If non zero, reset some value in the VPB when processing it. u16 reset_vpb; - u16 unk_05; + // If non zero, tells PCM8/PCM16 sample sources that the end of the voice + // has been reached and looping should be considered if enabled. + u16 end_reached; // If non zero, input samples to this VPB will be the fixed value from // VPB[33] (constant_sample_value). This is used when a voice is being @@ -482,6 +484,9 @@ struct ZeldaAudioRenderer::VPB // Simple saw wave at 100% amplitude and frequency controlled via the // resampling ratio. SRC_SAW_WAVE = 1, + // Samples stored in ARAM in PCM8 format, at an arbitrary sampling rate + // (resampling is applied). + SRC_PCM8_FROM_ARAM = 8, // Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded, // at an arbitrary sample rate (resampling is applied). SRC_AFC_HQ_FROM_ARAM = 9, @@ -549,9 +554,6 @@ void ZeldaAudioRenderer::PrepareFrame() if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0) PanicAlert("Zelda HLE using back mixing buffers"); - m_buf_back_left.fill(0); - m_buf_back_right.fill(0); - // TODO: Dolby/reverb mixing here. m_prepared = true; @@ -791,6 +793,12 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) break; } + case VPB::SRC_PCM8_FROM_ARAM: + DownloadPCM8SamplesFromARAM(raw_input_samples.data() + 4, vpb, + NeededRawSamplesCount(*vpb)); + Resample(vpb, raw_input_samples.data(), buffer); + break; + case VPB::SRC_AFC_HQ_FROM_ARAM: DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); @@ -861,44 +869,52 @@ void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst) vpb->current_pos_frac = pos & 0xFFF; } -void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( +void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( s16* dst, VPB* vpb, u16 requested_samples_count) { - u32 addr = vpb->GetBaseAddress() + vpb->current_position_h * sizeof (u16); - s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr); - - if (requested_samples_count > vpb->GetRemainingLength()) + if (vpb->done) { - s16 last_sample = 0; - for (u16 i = 0; i < vpb->GetRemainingLength(); ++i) - *dst++ = last_sample = Common::swap16(*src_ptr++); - for (u16 i = vpb->GetRemainingLength(); i < requested_samples_count; ++i) - *dst++ = last_sample; - - vpb->current_position_h += vpb->GetRemainingLength(); - vpb->SetRemainingLength(0); - vpb->done = true; + for (u16 i = 0; i < requested_samples_count; ++i) + dst[i] = 0; + return; } - else + + if (!vpb->reset_vpb) { - vpb->SetRemainingLength(vpb->GetRemainingLength() - requested_samples_count); - vpb->samples_before_loop = vpb->loop_start_position_h - vpb->current_position_h; - if (requested_samples_count <= vpb->samples_before_loop) + vpb->end_reached = false; + } + while (requested_samples_count) + { + if (vpb->end_reached) { - for (u16 i = 0; i < requested_samples_count; ++i) - *dst++ = Common::swap16(*src_ptr++); - vpb->current_position_h += requested_samples_count; - } - else - { - for (u16 i = 0; i < vpb->samples_before_loop; ++i) - *dst++ = Common::swap16(*src_ptr++); - vpb->SetBaseAddress(vpb->GetLoopAddress()); - src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetLoopAddress()); - for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i) - *dst++ = Common::swap16(*src_ptr++); - vpb->current_position_h = requested_samples_count - vpb->samples_before_loop; + vpb->end_reached = false; + if (!vpb->is_looping) + { + for (u16 i = 0; i < requested_samples_count; ++i) + dst[i] = 0; + vpb->done = true; + break; + } + vpb->SetCurrentPosition(vpb->GetLoopAddress()); } + + vpb->SetRemainingLength( + vpb->GetLoopStartPosition() - vpb->GetCurrentPosition()); + vpb->SetCurrentARAMAddr( + vpb->GetBaseAddress() + vpb->GetCurrentPosition()); + + s8* src_ptr = (s8*)DSP::GetARAMPtr() + vpb->GetCurrentARAMAddr(); + u16 samples_to_download = std::min(vpb->GetRemainingLength(), + (u32)requested_samples_count); + + for (u16 i = 0; i < samples_to_download; ++i) + *dst++ = *src_ptr++ << 8; + + vpb->SetRemainingLength(vpb->GetRemainingLength() - samples_to_download); + vpb->SetCurrentARAMAddr(vpb->GetCurrentARAMAddr() + samples_to_download); + requested_samples_count -= samples_to_download; + if (!vpb->GetRemainingLength()) + vpb->end_reached = true; } } @@ -1065,8 +1081,50 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) } } +void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( + s16* dst, VPB* vpb, u16 requested_samples_count) +{ + u32 addr = vpb->GetBaseAddress() + vpb->current_position_h * sizeof (u16); + s16* src_ptr = (s16*)HLEMemory_Get_Pointer(addr); + + if (requested_samples_count > vpb->GetRemainingLength()) + { + s16 last_sample = 0; + for (u16 i = 0; i < vpb->GetRemainingLength(); ++i) + *dst++ = last_sample = Common::swap16(*src_ptr++); + for (u16 i = vpb->GetRemainingLength(); i < requested_samples_count; ++i) + *dst++ = last_sample; + + vpb->current_position_h += vpb->GetRemainingLength(); + vpb->SetRemainingLength(0); + vpb->done = true; + } + else + { + vpb->SetRemainingLength(vpb->GetRemainingLength() - requested_samples_count); + vpb->samples_before_loop = vpb->loop_start_position_h - vpb->current_position_h; + if (requested_samples_count <= vpb->samples_before_loop) + { + for (u16 i = 0; i < requested_samples_count; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->current_position_h += requested_samples_count; + } + else + { + for (u16 i = 0; i < vpb->samples_before_loop; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->SetBaseAddress(vpb->GetLoopAddress()); + src_ptr = (s16*)HLEMemory_Get_Pointer(vpb->GetLoopAddress()); + for (u16 i = vpb->samples_before_loop; i < requested_samples_count; ++i) + *dst++ = Common::swap16(*src_ptr++); + vpb->current_position_h = requested_samples_count - vpb->samples_before_loop; + } + } +} + void ZeldaAudioRenderer::DoState(PointerWrap& p) { + // TODO(delroth): Add all the state here. p.Do(m_output_lbuf_addr); p.Do(m_output_rbuf_addr); } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 7dbfadbf82..062600eb88 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -121,15 +121,19 @@ private: // Coefficients used for resampling. std::array m_resampling_coeffs{}; - // Downloads samples from MRAM while handling appropriate length / looping - // behavior. - void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); + // Downloads PCM8 encoded samples from ARAM. Handles looping and other + // parameters appropriately. + void DownloadPCM8SamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); - // Download AFC encoded samples from ARAM and decode them. Handles looping + // Downloads AFC encoded samples from ARAM and decode them. Handles looping // and other parameters appropriately. void DownloadAFCSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); void DecodeAFC(VPB* vpb, s16* dst, size_t block_count); std::array m_afc_coeffs{}; + + // Downloads samples from MRAM while handling appropriate length / looping + // behavior. + void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); }; class ZeldaUCode : public UCodeInterface From 99447315161b21dd4658ce2857a0e5b02f81d75d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 02:18:36 +0100 Subject: [PATCH 17/59] Zelda HLE: Add reverb emulation. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 185 ++++++++++++++++++-- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 30 ++++ 2 files changed, 198 insertions(+), 17 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8a6d1b216c..7026727704 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -222,6 +222,8 @@ void ZeldaUCode::RunPendingCommands() resampling_coeffs[i] = Common::swap16(data_ptr[i]); m_renderer.SetResamplingCoeffs(std::move(resampling_coeffs)); + // TODO: 0x100 more words here to figure out. + std::array sine_table; for (size_t i = 0; i < 0x80; ++i) sine_table[i] = Common::swap16(data_ptr[0x200 + i]); @@ -233,8 +235,9 @@ void ZeldaUCode::RunPendingCommands() afc_coeffs[i] = Common::swap16(afc_coeffs_ptr[i]); m_renderer.SetAfcCoeffs(std::move(afc_coeffs)); + m_renderer.SetReverbPBBaseAddress(Read32()); + SendCommandAck(CommandAck::STANDARD, sync); - Read32(); break; } @@ -268,7 +271,7 @@ void ZeldaUCode::RunPendingCommands() void ZeldaUCode::SendCommandAck(CommandAck ack_type, u16 sync_value) { - u32 ack_mail; + u32 ack_mail = 0; switch (ack_type) { case CommandAck::STANDARD: ack_mail = DSP_SYNC; break; @@ -535,6 +538,29 @@ struct ZeldaAudioRenderer::VPB u16 padding[SIZE_IN_WORDS]; }; + +struct ReverbPB +{ + // If zero, skip this reverb PB. + u16 enabled; + // Size of the circular buffer in MRAM, expressed in number of 0x50 samples + // blocks (0xA0 bytes). + u16 circular_buffer_size; + // Base address of the circular buffer in MRAM. + u16 circular_buffer_base_h; + u16 circular_buffer_base_l; + + struct Destination + { + u16 buffer_id; // See VPB::Channel::id. + u16 volume; // 1.15 format. + }; + Destination dest[2]; + + // Coefficients for an 8-tap filter applied to each reverb buffer before + // adding its data to the destination. + s16 filter_coefs[8]; +}; #pragma pack(pop) void ZeldaAudioRenderer::PrepareFrame() @@ -554,11 +580,146 @@ void ZeldaAudioRenderer::PrepareFrame() if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0) PanicAlert("Zelda HLE using back mixing buffers"); - // TODO: Dolby/reverb mixing here. + // Add reverb data from previous frame. + ApplyReverb(false); + AddBuffersWithVolume(m_buf_front_left_reverb.data(), + m_buf_back_left_reverb.data(), + 0x50, 0x7FFF); + AddBuffersWithVolume(m_buf_front_right_reverb.data(), + m_buf_back_left_reverb.data(), + 0x50, 0xB820); + AddBuffersWithVolume(m_buf_front_left_reverb.data(), + m_buf_back_right_reverb.data() + 0x28, + 0x28, 0xB820); + AddBuffersWithVolume(m_buf_front_right_reverb.data(), + m_buf_back_left_reverb.data() + 0x28, + 0x28, 0x7FFF); + m_buf_back_left_reverb.fill(0); + m_buf_back_right_reverb.fill(0); m_prepared = true; } +void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) +{ + if (!m_reverb_pb_base_addr) + { + PanicAlert("Trying to apply reverb without available parameters."); + return; + } + + // Each of the 4 RPBs maps to one of these buffers. + MixingBuffer* reverb_buffers[4] = { + &m_buf_unk0_reverb, + &m_buf_unk1_reverb, + &m_buf_front_left_reverb, + &m_buf_front_right_reverb, + }; + std::array* last8_samples_buffers[4] = { + &m_buf_unk0_reverb_last8, + &m_buf_unk1_reverb_last8, + &m_buf_front_left_reverb_last8, + &m_buf_front_right_reverb_last8, + }; + + u16* rpb_base_ptr = (u16*)HLEMemory_Get_Pointer(m_reverb_pb_base_addr); + for (u16 rpb_idx = 0; rpb_idx < 4; ++rpb_idx) + { + ReverbPB rpb; + u16* rpb_raw_ptr = reinterpret_cast(&rpb); + for (size_t i = 0; i < sizeof (ReverbPB) / 2; ++i) + rpb_raw_ptr[i] = Common::swap16(rpb_base_ptr[rpb_idx * sizeof (ReverbPB) / 2 + i]); + + if (!rpb.enabled) + continue; + + u16 mram_buffer_idx = m_reverb_pb_frames_count[rpb_idx]; + + u32 mram_addr = ((rpb.circular_buffer_base_h << 16) | + rpb.circular_buffer_base_l) + + mram_buffer_idx * 0x50 * sizeof (s16); + s16* mram_ptr = (s16*)HLEMemory_Get_Pointer(mram_addr); + + if (!post_rendering) + { + // 8 more samples because of the filter order. The first 8 samples + // are the last 8 samples of the previous frame. + std::array buffer; + for (u16 i = 0; i < 8; ++i) + buffer[i] = (*last8_samples_buffers[rpb_idx])[i]; + + for (u16 i = 0; i < 0x50; ++i) + buffer[8 + i] = Common::swap16(mram_ptr[i]); + + for (u16 i = 0; i < 8; ++i) + (*last8_samples_buffers[rpb_idx])[i] = buffer[0x50 + i]; + + // Filter the buffer using provided coefficients. + for (u16 i = 0; i < 0x50; ++i) + { + s32 sample = 0; + for (u16 j = 0; j < 8; ++j) + sample += (s32)buffer[i + j] * rpb.filter_coefs[j]; + sample >>= 15; + MathUtil::Clamp(&sample, -0x8000, 0x7fff); + buffer[i] = sample; + } + + for (const auto& dest : rpb.dest) + { + if (dest.buffer_id == 0) + continue; + + MixingBuffer* dest_buffer = BufferForID(dest.buffer_id); + if (!dest_buffer) + { + PanicAlert("RPB mixing to an unknown buffer: %04x", dest.buffer_id); + continue; + } + AddBuffersWithVolume(dest_buffer->data(), buffer.data(), + 0x50, dest.volume); + } + + // TODO: If "enabled" & 2, the filtering should be done post and + // not pre mixing. + if (rpb.enabled & 2) + PanicAlert("RPB wants post filtering."); + + for (u16 i = 0; i < 0x50; ++i) + (*reverb_buffers[rpb_idx])[i] = buffer[i]; + } + else + { + MixingBuffer* buffer = reverb_buffers[rpb_idx]; + + // Upload the reverb data to RAM. + for (auto sample : *buffer) + *mram_ptr++ = Common::swap16(sample); + + mram_buffer_idx = (mram_buffer_idx + 1) % rpb.circular_buffer_size; + m_reverb_pb_frames_count[rpb_idx] = mram_buffer_idx; + } + } +} + +ZeldaAudioRenderer::MixingBuffer* ZeldaAudioRenderer::BufferForID(u16 buffer_id) +{ + switch (buffer_id) + { + case 0x0D00: return &m_buf_front_left; + case 0x0D60: return &m_buf_front_right; + case 0x0F40: return &m_buf_back_left; + case 0x0CA0: return &m_buf_back_right; + case 0x0E80: return &m_buf_front_left_reverb; + case 0x0EE0: return &m_buf_front_right_reverb; + case 0x0C00: return &m_buf_back_left_reverb; + case 0x0C50: return &m_buf_back_right_reverb; + case 0x0DC0: return &m_buf_unk0_reverb; + case 0x0E20: return &m_buf_unk1_reverb; + default: return nullptr; + } +} + void ZeldaAudioRenderer::AddVoice(u16 voice_id) { VPB vpb; @@ -656,18 +817,6 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) vpb.done = true; } - // Map buffer "IDs"/addresses to our emulated buffers. - std::map buffers = { - { 0x0D00, &m_buf_front_left }, - { 0x0D60, &m_buf_front_right }, - { 0x0F40, &m_buf_back_left }, - { 0x0CA0, &m_buf_back_right }, - { 0x0E80, &m_buf_front_left_reverb }, - { 0x0EE0, &m_buf_front_right_reverb }, - { 0x0C00, &m_buf_back_left_reverb }, - { 0x0C50, &m_buf_back_right_reverb }, - }; - for (auto& channel : vpb.channels) { if (!channel.id) @@ -683,10 +832,10 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) if (!channel.current_volume && !volume_step) continue; - MixingBuffer* dst_buffer = buffers[channel.id]; + MixingBuffer* dst_buffer = BufferForID(channel.id); if (!dst_buffer) { - ERROR_LOG(DSPHLE, "Mixing to an unmapped buffer: %04x", channel.id); + PanicAlert("Mixing to an unmapped buffer: %04x", channel.id); continue; } @@ -724,6 +873,8 @@ void ZeldaAudioRenderer::FinalizeFrame() // TODO: Some more Dolby mixing. + ApplyReverb(true); + m_prepared = false; } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 062600eb88..d90ce1794e 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -19,6 +19,7 @@ public: void SetResamplingCoeffs(std::array&& coeffs) { m_resampling_coeffs = coeffs; } void SetAfcCoeffs(std::array&& coeffs) { m_afc_coeffs = coeffs; } void SetVPBBaseAddress(u32 addr) { m_vpb_base_addr = addr; } + void SetReverbPBBaseAddress(u32 addr) { m_reverb_pb_base_addr = addr; } void SetOutputVolume(u16 volume) { m_output_volume = volume; } void SetOutputLeftBufferAddr(u32 addr) { m_output_lbuf_addr = addr; } void SetOutputRightBufferAddr(u32 addr) { m_output_rbuf_addr = addr; } @@ -76,6 +77,18 @@ private: return vol; } + // Does not use std::array because it needs to be able to process partial + // buffers. Volume is in 1.15 format. + void AddBuffersWithVolume(s16* dst, const s16* src, size_t count, u16 vol) + { + while (count--) + { + s32 vol_src = ((s32)*src++ * (s32)vol) >> 15; + MathUtil::Clamp(&vol_src, -0x8000, 0x7fff); + *dst++ += vol_src; + } + } + // Whether the frame needs to be prepared or not. bool m_prepared = false; @@ -96,6 +109,12 @@ private: MixingBuffer m_buf_front_right_reverb{}; MixingBuffer m_buf_back_left_reverb{}; MixingBuffer m_buf_back_right_reverb{}; + MixingBuffer m_buf_unk0_reverb{}; + MixingBuffer m_buf_unk1_reverb{}; + + // Maps a buffer "ID" (really, their address in the DSP DRAM...) to our + // buffers. Returns nullptr if no match is found. + MixingBuffer* BufferForID(u16 buffer_id); // Base address where VPBs are stored linearly in RAM. u32 m_vpb_base_addr; @@ -134,6 +153,17 @@ private: // Downloads samples from MRAM while handling appropriate length / looping // behavior. void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count); + + // Applies the reverb effect to Dolby mixed voices based on a set of + // per-buffer parameters. Is called twice: once before frame rendering and + // once after. + void ApplyReverb(bool post_rendering); + std::array m_reverb_pb_frames_count{}; + std::array m_buf_unk0_reverb_last8{}; + std::array m_buf_unk1_reverb_last8{}; + std::array m_buf_front_left_reverb_last8{}; + std::array m_buf_front_right_reverb_last8{}; + u32 m_reverb_pb_base_addr = 0; }; class ZeldaUCode : public UCodeInterface From b672788548fecbe7f4c0e6d93921509ba5f2f828 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 06:20:28 +0100 Subject: [PATCH 18/59] Zelda HLE: Skip command words that are not commands. Zelda Twilight Princess (GC) seems to push null words into the command buffer. The command handler in the UCode ignores initial command mails that do not have the MSB set. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 7026727704..8a2dd4229b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -173,13 +173,16 @@ void ZeldaUCode::RunPendingCommands() while (m_pending_commands_count) { - m_pending_commands_count--; - u32 cmd_mail = Read32(); + if (!(cmd_mail & 0x80000000)) + continue; + u32 command = (cmd_mail >> 24) & 0x7f; u32 sync = cmd_mail >> 16; u32 extra_data = cmd_mail & 0xFFFF; + m_pending_commands_count--; + switch (command) { case 0x00: From 6c61ee6278a7a2936a92087841a973bffa95a624 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 06:21:59 +0100 Subject: [PATCH 19/59] Zelda HLE: Initial support for Zelda Twilight Princess (GC) Very close to the TWW version of the UCode, haven't determined any differences yet (but I'm sure that will come soon). Works well enough to reach ingame without any errors other than a few volume issues. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index 7d607fd6d1..c58d4a3571 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -53,6 +53,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) return new AXUCode(dsphle, crc); case 0x86840740: // Zelda WW - US + case 0x6CA33A6D: // Zelda TP GC - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8a2dd4229b..61c92aaa8a 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -15,7 +15,8 @@ // * Super Mario Sunshine (type ????, CRC ????) // * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) // * The Legend of Zelda: The Wind Waker (type DAC, CRC 86840740) -// * The Legend of Zelda: Twilight Princess (type ????, CRC ????) +// * The Legend of Zelda: Twilight Princess / GC (type DAC, CRC 6CA33A6D) +// * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) #include "Core/ConfigManager.h" #include "Core/HW/DSPHLE/MailHandler.h" From bfff0a72dfada81a0f0844c42b93c8f29f59eafa Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 17:08:57 +0100 Subject: [PATCH 20/59] Zelda HLE: Implement sample sources 4, 7, 11 and 12. Just 4 different versions of the same sample source, using a different constant pattern uploaded to the DSP during command 01. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 44 ++++++++++++++++++++- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 4 ++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 61c92aaa8a..afb3bd3419 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -226,7 +226,10 @@ void ZeldaUCode::RunPendingCommands() resampling_coeffs[i] = Common::swap16(data_ptr[i]); m_renderer.SetResamplingCoeffs(std::move(resampling_coeffs)); - // TODO: 0x100 more words here to figure out. + std::array const_patterns; + for (size_t i = 0; i < 0x100; ++i) + const_patterns[i] = Common::swap16(data_ptr[0x100 + i]); + m_renderer.SetConstPatterns(std::move(const_patterns)); std::array sine_table; for (size_t i = 0; i < 0x80; ++i) @@ -491,6 +494,15 @@ struct ZeldaAudioRenderer::VPB // Simple saw wave at 100% amplitude and frequency controlled via the // resampling ratio. SRC_SAW_WAVE = 1, + + // Breaking the numerical ordering for these, but they are all related. + // Simple pattern stored in the data downloaded by command 01. Playback + // frequency is controlled by the resampling ratio. + SRC_CONST_PATTERN_0 = 7, + SRC_CONST_PATTERN_1 = 4, + SRC_CONST_PATTERN_2 = 11, + SRC_CONST_PATTERN_3 = 12, + // Samples stored in ARAM in PCM8 format, at an arbitrary sampling rate // (resampling is applied). SRC_PCM8_FROM_ARAM = 8, @@ -948,6 +960,36 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) break; } + case VPB::SRC_CONST_PATTERN_0: + case VPB::SRC_CONST_PATTERN_1: + case VPB::SRC_CONST_PATTERN_2: + case VPB::SRC_CONST_PATTERN_3: + { + const u16 PATTERN_SIZE = 0x40; + + std::map samples_source_to_pattern = { + { VPB::SRC_CONST_PATTERN_0, 0 }, + { VPB::SRC_CONST_PATTERN_1, 1 }, + { VPB::SRC_CONST_PATTERN_2, 2 }, + { VPB::SRC_CONST_PATTERN_3, 3 }, + }; + u16 pattern_idx = samples_source_to_pattern[vpb->samples_source_type]; + u16 pattern_offset = pattern_idx * PATTERN_SIZE; + s16* pattern = m_const_patterns.data() + pattern_offset; + + u32 pos = vpb->current_pos_frac << 6; // log2(PATTERN_SIZE) + u32 step = vpb->resampling_ratio << 5; + + for (size_t i = 0; i < buffer->size(); ++i) + { + (*buffer)[i] = pattern[pos >> 16]; + pos = (pos + step) % (PATTERN_SIZE << 16); + } + + vpb->current_pos_frac = pos >> 6; + break; + } + case VPB::SRC_PCM8_FROM_ARAM: DownloadPCM8SamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index d90ce1794e..736051fca0 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -16,6 +16,7 @@ public: void FinalizeFrame(); void SetSineTable(std::array&& sine_table) { m_sine_table = sine_table; } + void SetConstPatterns(std::array&& patterns) { m_const_patterns = patterns; } void SetResamplingCoeffs(std::array&& coeffs) { m_resampling_coeffs = coeffs; } void SetAfcCoeffs(std::array&& coeffs) { m_afc_coeffs = coeffs; } void SetVPBBaseAddress(u32 addr) { m_vpb_base_addr = addr; } @@ -125,6 +126,9 @@ private: // [0.0;pi/4] (sin(x) in [1.0;0.0]), in 1.15 fixed format. std::array m_sine_table{}; + // Const patterns used for some voice samples source. 4 x 0x40 samples. + std::array m_const_patterns{}; + // Fills up a buffer with the input samples for a voice, represented by its // VPB. void LoadInputSamples(MixingBuffer* buffer, VPB* vpb); From ea1ac5f5969ad9cdbfa254744eaf9d146583cee7 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 18:11:33 +0100 Subject: [PATCH 21/59] Zelda HLE: Add save state support. Also fix an inconsistency in the spelling of "coeffs". --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 34 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index afb3bd3419..c519b8f908 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -575,7 +575,7 @@ struct ReverbPB // Coefficients for an 8-tap filter applied to each reverb buffer before // adding its data to the destination. - s16 filter_coefs[8]; + s16 filter_coeffs[8]; }; #pragma pack(pop) @@ -675,7 +675,7 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) { s32 sample = 0; for (u16 j = 0; j < 8; ++j) - sample += (s32)buffer[i + j] * rpb.filter_coefs[j]; + sample += (s32)buffer[i + j] * rpb.filter_coeffs[j]; sample >>= 15; MathUtil::Clamp(&sample, -0x8000, 0x7fff); buffer[i] = sample; @@ -1321,7 +1321,35 @@ void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( void ZeldaAudioRenderer::DoState(PointerWrap& p) { - // TODO(delroth): Add all the state here. + p.Do(m_prepared); + p.Do(m_output_lbuf_addr); p.Do(m_output_rbuf_addr); + p.Do(m_output_volume); + + p.Do(m_buf_front_left); + p.Do(m_buf_front_right); + p.Do(m_buf_back_left); + p.Do(m_buf_back_right); + p.Do(m_buf_front_left_reverb); + p.Do(m_buf_front_right_reverb); + p.Do(m_buf_back_left_reverb); + p.Do(m_buf_back_right_reverb); + p.Do(m_buf_unk0_reverb); + p.Do(m_buf_unk1_reverb); + + p.Do(m_resampling_coeffs); + p.Do(m_const_patterns); + p.Do(m_sine_table); + p.Do(m_afc_coeffs); + + p.Do(m_vpb_base_addr); + + p.Do(m_reverb_pb_base_addr); + p.Do(m_reverb_pb_frames_count); + p.Do(m_buf_unk0_reverb_last8); + p.Do(m_buf_unk1_reverb_last8); + p.Do(m_buf_front_left_reverb_last8); + p.Do(m_buf_front_right_reverb_last8); + } From 0dc5a925b0a1b995fa0dd50899690b4f720611bf Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 21 Dec 2014 18:45:34 +0100 Subject: [PATCH 22/59] Zelda HLE: Add initial support for Wii DAC and SMG1. SMG1 boots but quickly PanicAlerts due to an unimplemented sample source. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 3 ++- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 25 ++++++++++++++++---- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 12 ++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index c58d4a3571..c3f9a34d09 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -53,7 +53,8 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) return new AXUCode(dsphle, crc); case 0x86840740: // Zelda WW - US - case 0x6CA33A6D: // Zelda TP GC - US + case 0x6ca33a6d: // Zelda TP GC - US + case 0xd643001f: // Super Mario Galaxy - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index c519b8f908..9379243c9d 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -10,7 +10,7 @@ // * Mario Kart: Double Dash!! (type ????, CRC ????) // * Pikmin (type ????, CRC ????) // * Pikmin 2 (type ????, CRC ????) -// * Super Mario Galaxy (type ????, CRC ????) +// * Super Mario Galaxy (type Wii-DAC, CRC D643001F) // * Super Mario Galaxy 2 (type ????, CRC ????) // * Super Mario Sunshine (type ????, CRC ????) // * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) @@ -191,7 +191,6 @@ void ZeldaUCode::RunPendingCommands() case 0x0A: case 0x0B: case 0x0C: - case 0x0E: case 0x0F: // NOP commands. Log anyway in case we encounter a new version // where these are not NOPs anymore. @@ -268,6 +267,16 @@ void ZeldaUCode::RunPendingCommands() SendCommandAck(CommandAck::STANDARD, sync); break; + // Command 0E: Sets the base address of the ARAM for Wii UCodes. Used + // because the Wii does not have an ARAM, so it simulates it with MRAM + // and DMAs. + case 0x0E: + if (!IsWiiDAC()) + PanicAlert("Setting base ARAM addr on non Wii DAC."); + m_renderer.SetARAMBaseAddr(Read32()); + SendCommandAck(CommandAck::STANDARD, sync); + break; + default: NOTICE_LOG(DSPHLE, "Received a non-existing command (%d), halting.", command); SetMailState(MailState::HALTED); @@ -1066,6 +1075,14 @@ void ZeldaAudioRenderer::Resample(VPB* vpb, const s16* src, MixingBuffer* dst) vpb->current_pos_frac = pos & 0xFFF; } +void* ZeldaAudioRenderer::GetARAMPtr() const +{ + if (m_aram_base_addr) + return HLEMemory_Get_Pointer(m_aram_base_addr); + else + return DSP::GetARAMPtr(); +} + void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( s16* dst, VPB* vpb, u16 requested_samples_count) { @@ -1100,7 +1117,7 @@ void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( vpb->SetCurrentARAMAddr( vpb->GetBaseAddress() + vpb->GetCurrentPosition()); - s8* src_ptr = (s8*)DSP::GetARAMPtr() + vpb->GetCurrentARAMAddr(); + s8* src_ptr = (s8*)GetARAMPtr() + vpb->GetCurrentARAMAddr(); u16 samples_to_download = std::min(vpb->GetRemainingLength(), (u32)requested_samples_count); @@ -1230,7 +1247,7 @@ void ZeldaAudioRenderer::DownloadAFCSamplesFromARAM( void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) { u32 addr = vpb->GetCurrentARAMAddr(); - u8* src = (u8*)DSP::GetARAMPtr() + addr; + u8* src = (u8*)GetARAMPtr() + addr; vpb->SetCurrentARAMAddr(addr + (u32)block_count * vpb->samples_source_type); for (size_t b = 0; b < block_count; ++b) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 736051fca0..ff40aa68af 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -24,6 +24,7 @@ public: void SetOutputVolume(u16 volume) { m_output_volume = volume; } void SetOutputLeftBufferAddr(u32 addr) { m_output_lbuf_addr = addr; } void SetOutputRightBufferAddr(u32 addr) { m_output_rbuf_addr = addr; } + void SetARAMBaseAddr(u32 addr) { m_aram_base_addr = addr; } void DoState(PointerWrap& p); @@ -144,6 +145,12 @@ private: // Coefficients used for resampling. std::array m_resampling_coeffs{}; + // If non zero, base MRAM address for sound data transfers from ARAM. On + // the Wii, this points to some MRAM location since there is no ARAM to be + // used. If zero, use the top of ARAM. + u32 m_aram_base_addr = 0; + void* GetARAMPtr() const; + // Downloads PCM8 encoded samples from ARAM. Handles looping and other // parameters appropriately. void DownloadPCM8SamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); @@ -182,6 +189,11 @@ public: void DoState(PointerWrap &p) override; + bool IsWiiDAC() const + { + return m_crc == 0xd643001f; + } + private: // UCode state machine. The control flow in the Zelda UCode family is quite // complex, using interrupt handlers heavily to handle incoming messages From 8b9b9f033abdc36d62c4933fe8cde7c64d1d2fb2 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 01:25:14 +0100 Subject: [PATCH 23/59] Zelda HLE: Fix and genericize PCM ARAM loading functions. Now also handles PCM16, and works for longer sounds. SMG1 main menu outputs some sensible audio now, though volume seems slightly off. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 45 +++++++++++++-------- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 4 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 9379243c9d..66b0dc7e31 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -503,7 +503,6 @@ struct ZeldaAudioRenderer::VPB // Simple saw wave at 100% amplitude and frequency controlled via the // resampling ratio. SRC_SAW_WAVE = 1, - // Breaking the numerical ordering for these, but they are all related. // Simple pattern stored in the data downloaded by command 01. Playback // frequency is controlled by the resampling ratio. @@ -511,13 +510,15 @@ struct ZeldaAudioRenderer::VPB SRC_CONST_PATTERN_1 = 4, SRC_CONST_PATTERN_2 = 11, SRC_CONST_PATTERN_3 = 12, - // Samples stored in ARAM in PCM8 format, at an arbitrary sampling rate // (resampling is applied). SRC_PCM8_FROM_ARAM = 8, // Samples stored in ARAM at a rate of 16 samples/9 bytes, AFC encoded, // at an arbitrary sample rate (resampling is applied). SRC_AFC_HQ_FROM_ARAM = 9, + // Samples stored in ARAM in PCM16 format, at an arbitrary sampling + // rate (resampling is applied). + SRC_PCM16_FROM_ARAM = 16, // Samples stored in MRAM at an arbitrary sample rate (resampling is // applied, unlike PCM16_FROM_MRAM_RAW). SRC_PCM16_FROM_MRAM = 33, @@ -622,6 +623,8 @@ void ZeldaAudioRenderer::PrepareFrame() m_buf_back_left_reverb.fill(0); m_buf_back_right_reverb.fill(0); + // TODO: Prepare patterns 2/3 - they are not constant unlike 0/1. + m_prepared = true; } @@ -1000,8 +1003,8 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) } case VPB::SRC_PCM8_FROM_ARAM: - DownloadPCM8SamplesFromARAM(raw_input_samples.data() + 4, vpb, - NeededRawSamplesCount(*vpb)); + DownloadPCMSamplesFromARAM(raw_input_samples.data() + 4, vpb, + NeededRawSamplesCount(*vpb)); Resample(vpb, raw_input_samples.data(), buffer); break; @@ -1011,6 +1014,12 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) Resample(vpb, raw_input_samples.data(), buffer); break; + case VPB::SRC_PCM16_FROM_ARAM: + DownloadPCMSamplesFromARAM(raw_input_samples.data() + 4, vpb, + NeededRawSamplesCount(*vpb)); + Resample(vpb, raw_input_samples.data(), buffer); + break; + case VPB::SRC_PCM16_FROM_MRAM: DownloadRawSamplesFromMRAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); @@ -1083,8 +1092,8 @@ void* ZeldaAudioRenderer::GetARAMPtr() const return DSP::GetARAMPtr(); } -void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( - s16* dst, VPB* vpb, u16 requested_samples_count) +template +void ZeldaAudioRenderer::DownloadPCMSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count) { if (vpb->done) { @@ -1093,10 +1102,15 @@ void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( return; } - if (!vpb->reset_vpb) + if (vpb->reset_vpb) { - vpb->end_reached = false; + vpb->SetRemainingLength( + vpb->GetLoopStartPosition() - vpb->GetCurrentPosition()); + vpb->SetCurrentARAMAddr( + vpb->GetBaseAddress() + vpb->GetCurrentPosition() * sizeof (T)); } + + vpb->end_reached = false; while (requested_samples_count) { if (vpb->end_reached) @@ -1110,22 +1124,21 @@ void ZeldaAudioRenderer::DownloadPCM8SamplesFromARAM( break; } vpb->SetCurrentPosition(vpb->GetLoopAddress()); + vpb->SetRemainingLength( + vpb->GetLoopStartPosition() - vpb->GetCurrentPosition()); + vpb->SetCurrentARAMAddr( + vpb->GetBaseAddress() + vpb->GetCurrentPosition() * sizeof (T)); } - vpb->SetRemainingLength( - vpb->GetLoopStartPosition() - vpb->GetCurrentPosition()); - vpb->SetCurrentARAMAddr( - vpb->GetBaseAddress() + vpb->GetCurrentPosition()); - - s8* src_ptr = (s8*)GetARAMPtr() + vpb->GetCurrentARAMAddr(); + T* src_ptr = (T*)((u8*)GetARAMPtr() + vpb->GetCurrentARAMAddr()); u16 samples_to_download = std::min(vpb->GetRemainingLength(), (u32)requested_samples_count); for (u16 i = 0; i < samples_to_download; ++i) - *dst++ = *src_ptr++ << 8; + *dst++ = Common::FromBigEndian(*src_ptr++) << (16 - 8 * sizeof (T)); vpb->SetRemainingLength(vpb->GetRemainingLength() - samples_to_download); - vpb->SetCurrentARAMAddr(vpb->GetCurrentARAMAddr() + samples_to_download); + vpb->SetCurrentARAMAddr(vpb->GetCurrentARAMAddr() + samples_to_download * sizeof (T)); requested_samples_count -= samples_to_download; if (!vpb->GetRemainingLength()) vpb->end_reached = true; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index ff40aa68af..dd647e9712 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -151,9 +151,9 @@ private: u32 m_aram_base_addr = 0; void* GetARAMPtr() const; - // Downloads PCM8 encoded samples from ARAM. Handles looping and other + // Downloads PCM encoded samples from ARAM. Handles looping and other // parameters appropriately. - void DownloadPCM8SamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); + template void DownloadPCMSamplesFromARAM(s16* dst, VPB* vpb, u16 requested_samples_count); // Downloads AFC encoded samples from ARAM and decode them. Handles looping // and other parameters appropriately. From c033395e28a3fc14a9d481b415bed30b657cdff3 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 02:22:26 +0100 Subject: [PATCH 24/59] Zelda HLE: Introduce behavior flags to handle UCode version differences. MAKE_DOLBY_LOUDER solves some of the volume issues that were happening in Zelda Twilight Princess and SMG1. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 85 ++++++++++++++------- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 13 ++-- 2 files changed, 66 insertions(+), 32 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 66b0dc7e31..8d28775350 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -2,32 +2,59 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -// Games that use this UCode (exhaustive list): -// * Animal Crossing (type ????, CRC ????) -// * Donkey Kong Jungle Beat (type ????, CRC ????) -// * IPL (type ????, CRC ????) -// * Luigi's Mansion (type ????, CRC ????) -// * Mario Kart: Double Dash!! (type ????, CRC ????) -// * Pikmin (type ????, CRC ????) -// * Pikmin 2 (type ????, CRC ????) -// * Super Mario Galaxy (type Wii-DAC, CRC D643001F) -// * Super Mario Galaxy 2 (type ????, CRC ????) -// * Super Mario Sunshine (type ????, CRC ????) -// * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) -// * The Legend of Zelda: The Wind Waker (type DAC, CRC 86840740) -// * The Legend of Zelda: Twilight Princess / GC (type DAC, CRC 6CA33A6D) -// * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) - #include "Core/ConfigManager.h" #include "Core/HW/DSPHLE/MailHandler.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/DSPHLE/UCodes/Zelda.h" +// These flags modify the behavior of the HLE implementation based on the UCode +// version. When introducing a new flag, please recheck the behavior of each +// UCode version. +enum ZeldaUCodeFlag +{ + // UCode for Wii where no ARAM is present. Instead of using ARAM, DMAs from + // MRAM are used to transfer sound data. + NO_ARAM = 0x00000001, + + // Multiply by two the computed Dolby positional volumes. Some UCodes do + // not do that (Zelda TWW for example), others do (Zelda TP, SMG). + MAKE_DOLBY_LOUDER = 0x00000002, +}; + +static const std::map UCODE_FLAGS = { + // The Legend of Zelda: The Wind Waker. + { 0x86840740, 0 }, + // The Legend of Zelda: Twilight Princess / GC. + { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, + // Super Mario Galaxy. + { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, + + // TODO: Other games that use this UCode (exhaustive list): + // * Animal Crossing (type ????, CRC ????) + // * Donkey Kong Jungle Beat (type ????, CRC ????) + // * IPL (type ????, CRC ????) + // * Luigi's Mansion (type ????, CRC ????) + // * Mario Kart: Double Dash!! (type ????, CRC ????) + // * Pikmin (type ????, CRC ????) + // * Pikmin 2 (type ????, CRC ????) + // * Super Mario Galaxy 2 (type ????, CRC ????) + // * Super Mario Sunshine (type ????, CRC ????) + // * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) + // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) +}; + ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) : UCodeInterface(dsphle, crc) { m_mail_handler.PushMail(DSP_INIT, true); m_mail_handler.PushMail(0xF3551111); // handshake + + auto it = UCODE_FLAGS.find(crc); + if (it == UCODE_FLAGS.end()) + PanicAlert("No flags definition found for Zelda CRC %08x", crc); + + m_flags = it->second; + m_renderer.SetFlags(m_flags); } ZeldaUCode::~ZeldaUCode() @@ -50,6 +77,7 @@ u32 ZeldaUCode::GetUpdateMs() void ZeldaUCode::DoState(PointerWrap &p) { + p.Do(m_flags); p.Do(m_mail_current_state); p.Do(m_mail_expected_cmd_mails); @@ -271,7 +299,7 @@ void ZeldaUCode::RunPendingCommands() // because the Wii does not have an ARAM, so it simulates it with MRAM // and DMAs. case 0x0E: - if (!IsWiiDAC()) + if (!(m_flags & NO_ARAM)) PanicAlert("Setting base ARAM addr on non Wii DAC."); m_renderer.SetARAMBaseAddr(Read32()); SendCommandAck(CommandAck::STANDARD, sync); @@ -779,11 +807,12 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) s16 front_volume = m_sine_table[vpb.GetDolbyVoiceY() ^ 0x7F]; // Compute volume for each quadrant. + u16 shift_factor = (m_flags & MAKE_DOLBY_LOUDER) ? 15 : 16; s16 quadrant_volumes[4] = { - (s16)((left_volume * front_volume) >> 16), - (s16)((left_volume * back_volume) >> 16), - (s16)((right_volume * front_volume) >> 16), - (s16)((right_volume * back_volume) >> 16), + (s16)((left_volume * front_volume) >> shift_factor), + (s16)((left_volume * back_volume) >> shift_factor), + (s16)((right_volume * front_volume) >> shift_factor), + (s16)((right_volume * back_volume) >> shift_factor), }; // Compute the volume delta for each sample to match the difference @@ -791,19 +820,19 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) s16 delta = vpb.dolby_volume_target - vpb.dolby_volume_current; s16 volume_deltas[4]; for (size_t i = 0; i < 4; ++i) - volume_deltas[i] = ((u16)quadrant_volumes[i] * delta) >> 16; + volume_deltas[i] = ((u16)quadrant_volumes[i] * delta) >> shift_factor; // Apply master volume to each quadrant. for (size_t i = 0; i < 4; ++i) - quadrant_volumes[i] = (quadrant_volumes[i] * vpb.dolby_volume_current) >> 16; + quadrant_volumes[i] = (quadrant_volumes[i] * vpb.dolby_volume_current) >> shift_factor; // Compute reverb volume and ramp deltas. s16 reverb_volumes[4], reverb_volume_deltas[4]; - s16 reverb_volume_factor = (vpb.dolby_volume_current * vpb.dolby_reverb_factor) >> 15; + s16 reverb_volume_factor = (vpb.dolby_volume_current * vpb.dolby_reverb_factor) >> (shift_factor - 1); for (size_t i = 0; i < 4; ++i) { - reverb_volumes[i] = (quadrant_volumes[i] * reverb_volume_factor) >> 15; - reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> 16; + reverb_volumes[i] = (quadrant_volumes[i] * reverb_volume_factor) >> shift_factor; + reverb_volume_deltas[i] = (volume_deltas[i] * vpb.dolby_reverb_factor) >> shift_factor; } struct { @@ -1351,6 +1380,7 @@ void ZeldaAudioRenderer::DownloadRawSamplesFromMRAM( void ZeldaAudioRenderer::DoState(PointerWrap& p) { + p.Do(m_flags); p.Do(m_prepared); p.Do(m_output_lbuf_addr); @@ -1373,9 +1403,10 @@ void ZeldaAudioRenderer::DoState(PointerWrap& p) p.Do(m_sine_table); p.Do(m_afc_coeffs); + p.Do(m_aram_base_addr); p.Do(m_vpb_base_addr); - p.Do(m_reverb_pb_base_addr); + p.Do(m_reverb_pb_frames_count); p.Do(m_buf_unk0_reverb_last8); p.Do(m_buf_unk1_reverb_last8); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index dd647e9712..e9126ef8a8 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -15,6 +15,7 @@ public: void AddVoice(u16 voice_id); void FinalizeFrame(); + void SetFlags(u32 flags) { m_flags = flags; } void SetSineTable(std::array&& sine_table) { m_sine_table = sine_table; } void SetConstPatterns(std::array&& patterns) { m_const_patterns = patterns; } void SetResamplingCoeffs(std::array&& coeffs) { m_resampling_coeffs = coeffs; } @@ -31,6 +32,9 @@ public: private: struct VPB; + // See Zelda.cpp for the list of possible flags. + u32 m_flags; + // Utility functions for audio operations. // Apply volume to a buffer. The volume is a fixed point integer, usually @@ -189,12 +193,11 @@ public: void DoState(PointerWrap &p) override; - bool IsWiiDAC() const - { - return m_crc == 0xd643001f; - } - private: + // Flags that alter the behavior of the UCode. See Zelda.cpp for complete + // list and explanation. + u32 m_flags; + // UCode state machine. The control flow in the Zelda UCode family is quite // complex, using interrupt handlers heavily to handle incoming messages // which, depending on the type, get handled immediately or are queued in a From 13ea54628d996023936043113ef983aa74c691d6 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 05:37:34 +0100 Subject: [PATCH 25/59] Zelda HLE: Add support for the light protocol. Used by a few titles (Luigi's Mansion, Animal Crossing) as well as the GameCube IPL/BIOS. Note that the IPL does not work yet because it mixes to unknown buffers. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 136 ++++++++++++++++--- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 4 + 3 files changed, 125 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index c3f9a34d09..31f89aac97 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -55,6 +55,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x86840740: // Zelda WW - US case 0x6ca33a6d: // Zelda TP GC - US case 0xd643001f: // Super Mario Galaxy - US + case 0x6ba3b3ea: // GC IPL - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8d28775350..67b5f15c01 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -19,6 +19,10 @@ enum ZeldaUCodeFlag // Multiply by two the computed Dolby positional volumes. Some UCodes do // not do that (Zelda TWW for example), others do (Zelda TP, SMG). MAKE_DOLBY_LOUDER = 0x00000002, + + // Light version of the UCode: no Dolby mixing, different synchronization + // protocol, etc. + LIGHT_PROTOCOL = 0x00000004, }; static const std::map UCODE_FLAGS = { @@ -28,11 +32,12 @@ static const std::map UCODE_FLAGS = { { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, + // GameCube IPL/BIOS. + { 0x6BA3B3EA, LIGHT_PROTOCOL }, // TODO: Other games that use this UCode (exhaustive list): // * Animal Crossing (type ????, CRC ????) // * Donkey Kong Jungle Beat (type ????, CRC ????) - // * IPL (type ????, CRC ????) // * Luigi's Mansion (type ????, CRC ????) // * Mario Kart: Double Dash!! (type ????, CRC ????) // * Pikmin (type ????, CRC ????) @@ -46,15 +51,22 @@ static const std::map UCODE_FLAGS = { ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) : UCodeInterface(dsphle, crc) { - m_mail_handler.PushMail(DSP_INIT, true); - m_mail_handler.PushMail(0xF3551111); // handshake - auto it = UCODE_FLAGS.find(crc); if (it == UCODE_FLAGS.end()) PanicAlert("No flags definition found for Zelda CRC %08x", crc); m_flags = it->second; m_renderer.SetFlags(m_flags); + + if (m_flags & LIGHT_PROTOCOL) + { + m_mail_handler.PushMail(0x88881111); + } + else + { + m_mail_handler.PushMail(DSP_INIT, true); + m_mail_handler.PushMail(0xF3551111); // handshake + } } ZeldaUCode::~ZeldaUCode() @@ -108,6 +120,14 @@ void ZeldaUCode::HandleMail(u32 mail) return; } + if (m_flags & LIGHT_PROTOCOL) + HandleMailLight(mail); + else + HandleMailDefault(mail); +} + +void ZeldaUCode::HandleMailDefault(u32 mail) +{ switch (m_mail_current_state) { case MailState::WAITING: @@ -191,6 +211,65 @@ void ZeldaUCode::HandleMail(u32 mail) } } +void ZeldaUCode::HandleMailLight(u32 mail) +{ + switch (m_mail_current_state) + { + case MailState::WAITING: + if (!(mail & 0x80000000)) + PanicAlert("Mail received in waiting state has MSB=0: %08x", mail); + + // Start of a command. We have to hardcode the number of mails required + // for each command - the alternative is to rewrite command handling as + // an asynchronous procedure, and we wouldn't want that, would we? + Write32(mail); + switch ((mail >> 24) & 0x7F) + { + case 0: m_mail_expected_cmd_mails = 0; break; + case 1: m_mail_expected_cmd_mails = 4; break; + case 2: m_mail_expected_cmd_mails = 2; break; + default: + PanicAlert("Received unknown command in light protocol: %08x", mail); + break; + } + if (m_mail_expected_cmd_mails) + { + SetMailState(MailState::WRITING_CMD); + } + else + { + m_pending_commands_count += 1; + RunPendingCommands(); + } + break; + + case MailState::WRITING_CMD: + Write32(mail); + if (--m_mail_expected_cmd_mails == 0) + { + m_pending_commands_count += 1; + SetMailState(MailState::WAITING); + RunPendingCommands(); + } + break; + + case MailState::RENDERING: + if (mail != 0) + PanicAlert("Sync mail is not zero: %08x", mail); + + // No per-voice syncing in the light protocol. + m_sync_max_voice_id = 0xFFFFFFFF; + m_sync_voice_skip_flags.fill(0xFFFFFFFF); + RenderAudio(); + DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); + break; + + case MailState::HALTED: + WARN_LOG(DSPHLE, "Received mail %08x while we're halted.", mail); + break; + } +} + void ZeldaUCode::RunPendingCommands() { if (RenderingInProgress() || !m_cmd_can_execute) @@ -286,7 +365,16 @@ void ZeldaUCode::RunPendingCommands() m_rendering_curr_frame = 0; m_rendering_curr_voice = 0; - RenderAudio(); + + if (m_flags & LIGHT_PROTOCOL) + { + SendCommandAck(CommandAck::STANDARD, m_rendering_requested_frames); + SetMailState(MailState::RENDERING); + } + else + { + RenderAudio(); + } return; // Command 0D: TODO: find a name and implement. @@ -315,17 +403,25 @@ void ZeldaUCode::RunPendingCommands() void ZeldaUCode::SendCommandAck(CommandAck ack_type, u16 sync_value) { - u32 ack_mail = 0; - switch (ack_type) + if (m_flags & LIGHT_PROTOCOL) { - case CommandAck::STANDARD: ack_mail = DSP_SYNC; break; - case CommandAck::DONE_RENDERING: ack_mail = DSP_FRAME_END; break; + // The light protocol uses the address of the command handler in the + // DSP code instead of the command id... go figure. + sync_value = 2 * ((sync_value >> 8) & 0x7F) + 0x62; + m_mail_handler.PushMail(0x80000000 | sync_value); } - m_mail_handler.PushMail(ack_mail, true); - - if (ack_type == CommandAck::STANDARD) + else { - m_mail_handler.PushMail(0xF3550000 | sync_value); + u32 ack_mail = 0; + switch (ack_type) + { + case CommandAck::STANDARD: ack_mail = DSP_SYNC; break; + case CommandAck::DONE_RENDERING: ack_mail = DSP_FRAME_END; break; + } + m_mail_handler.PushMail(ack_mail, true); + + if (ack_type == CommandAck::STANDARD) + m_mail_handler.PushMail(0xF3550000 | sync_value); } } @@ -358,7 +454,8 @@ void ZeldaUCode::RenderAudio() m_rendering_curr_voice++; } - SendCommandAck(CommandAck::STANDARD, 0xFF00 | m_rendering_curr_frame); + if (!(m_flags & LIGHT_PROTOCOL)) + SendCommandAck(CommandAck::STANDARD, 0xFF00 | m_rendering_curr_frame); m_renderer.FinalizeFrame(); @@ -367,8 +464,15 @@ void ZeldaUCode::RenderAudio() m_rendering_curr_frame++; } - SendCommandAck(CommandAck::DONE_RENDERING, 0); - m_cmd_can_execute = false; // Block command execution until ACK is received. + if (!(m_flags & LIGHT_PROTOCOL)) + { + SendCommandAck(CommandAck::DONE_RENDERING, 0); + m_cmd_can_execute = false; // Block command execution until ACK is received. + } + else + { + SetMailState(MailState::WAITING); + } } // Utility to define 32 bit accessors/modifiers methods based on two 16 bit diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index e9126ef8a8..d2d1bfe165 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -198,6 +198,10 @@ private: // list and explanation. u32 m_flags; + // Different mail handlers for different protocols. + void HandleMailDefault(u32 mail); + void HandleMailLight(u32 mail); + // UCode state machine. The control flow in the Zelda UCode family is quite // complex, using interrupt handlers heavily to handle incoming messages // which, depending on the type, get handled immediately or are queued in a From 8c85a8c8d9d80a4a70dabcc2f987767bb33a5fc0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 05:43:35 +0100 Subject: [PATCH 26/59] Zelda HLE: Fix reverb in the GC IPL. Adds a missing destination buffer, and support post-filtering. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 33 +++++++++++++-------- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 67b5f15c01..23083f9b93 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -814,16 +814,22 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) for (u16 i = 0; i < 8; ++i) (*last8_samples_buffers[rpb_idx])[i] = buffer[0x50 + i]; - // Filter the buffer using provided coefficients. - for (u16 i = 0; i < 0x50; ++i) - { - s32 sample = 0; - for (u16 j = 0; j < 8; ++j) - sample += (s32)buffer[i + j] * rpb.filter_coeffs[j]; - sample >>= 15; - MathUtil::Clamp(&sample, -0x8000, 0x7fff); - buffer[i] = sample; - } + auto ApplyFilter = [&]() { + // Filter the buffer using provided coefficients. + for (u16 i = 0; i < 0x50; ++i) + { + s32 sample = 0; + for (u16 j = 0; j < 8; ++j) + sample += (s32)buffer[i + j] * rpb.filter_coeffs[j]; + sample >>= 15; + MathUtil::Clamp(&sample, -0x8000, 0x7fff); + buffer[i] = sample; + } + }; + + // LSB set -> pre-filtering. + if (rpb.enabled & 1) + ApplyFilter(); for (const auto& dest : rpb.dest) { @@ -840,10 +846,9 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) 0x50, dest.volume); } - // TODO: If "enabled" & 2, the filtering should be done post and - // not pre mixing. + // LSB not set, bit 1 set -> post-filtering. if (rpb.enabled & 2) - PanicAlert("RPB wants post filtering."); + ApplyFilter(); for (u16 i = 0; i < 0x50; ++i) (*reverb_buffers[rpb_idx])[i] = buffer[i]; @@ -876,6 +881,7 @@ ZeldaAudioRenderer::MixingBuffer* ZeldaAudioRenderer::BufferForID(u16 buffer_id) case 0x0C50: return &m_buf_back_right_reverb; case 0x0DC0: return &m_buf_unk0_reverb; case 0x0E20: return &m_buf_unk1_reverb; + case 0x09A0: return &m_buf_unk0; // Used by the GC IPL as a reverb dest. default: return nullptr; } } @@ -1501,6 +1507,7 @@ void ZeldaAudioRenderer::DoState(PointerWrap& p) p.Do(m_buf_back_right_reverb); p.Do(m_buf_unk0_reverb); p.Do(m_buf_unk1_reverb); + p.Do(m_buf_unk0); p.Do(m_resampling_coeffs); p.Do(m_const_patterns); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index d2d1bfe165..b49ffe117e 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -117,6 +117,7 @@ private: MixingBuffer m_buf_back_right_reverb{}; MixingBuffer m_buf_unk0_reverb{}; MixingBuffer m_buf_unk1_reverb{}; + MixingBuffer m_buf_unk0{}; // Maps a buffer "ID" (really, their address in the DSP DRAM...) to our // buffers. Returns nullptr if no match is found. From 4ace79024df07dbf6486d5a688e7a3552a4281da Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 05:50:34 +0100 Subject: [PATCH 27/59] Zelda HLE: Add a missing mixing buffer used by GC IPL. Not completely sure what it's used for yet, but TWW has references to it so I'll get to it at some point. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 ++ Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 1 + 2 files changed, 3 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 23083f9b93..0754e1fc0c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -882,6 +882,7 @@ ZeldaAudioRenderer::MixingBuffer* ZeldaAudioRenderer::BufferForID(u16 buffer_id) case 0x0DC0: return &m_buf_unk0_reverb; case 0x0E20: return &m_buf_unk1_reverb; case 0x09A0: return &m_buf_unk0; // Used by the GC IPL as a reverb dest. + case 0x0FA0: return &m_buf_unk1; // Used by the GC IPL as a mixing dest. default: return nullptr; } } @@ -1508,6 +1509,7 @@ void ZeldaAudioRenderer::DoState(PointerWrap& p) p.Do(m_buf_unk0_reverb); p.Do(m_buf_unk1_reverb); p.Do(m_buf_unk0); + p.Do(m_buf_unk1); p.Do(m_resampling_coeffs); p.Do(m_const_patterns); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index b49ffe117e..7a3ad011a5 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -118,6 +118,7 @@ private: MixingBuffer m_buf_unk0_reverb{}; MixingBuffer m_buf_unk1_reverb{}; MixingBuffer m_buf_unk0{}; + MixingBuffer m_buf_unk1{}; // Maps a buffer "ID" (really, their address in the DSP DRAM...) to our // buffers. Returns nullptr if no match is found. From 1807c113b599a50c51a0dc66b8bd87bb05a4716a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 06:20:48 +0100 Subject: [PATCH 28/59] Zelda HLE: Support both NTSC and PAL IPL. Add a flag for UCodes that only have four non-Dolby mixing destinations (instead of the standard six destinations). NTSC IPL is still hopelessly broken. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 3 +- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 33 ++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index 31f89aac97..e0982ad3e8 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -55,7 +55,8 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x86840740: // Zelda WW - US case 0x6ca33a6d: // Zelda TP GC - US case 0xd643001f: // Super Mario Galaxy - US - case 0x6ba3b3ea: // GC IPL - US + case 0x6ba3b3ea: // GC IPL - PAL + case 0x24b22038: // GC IPL - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 0754e1fc0c..8ad7e2871b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -23,6 +23,10 @@ enum ZeldaUCodeFlag // Light version of the UCode: no Dolby mixing, different synchronization // protocol, etc. LIGHT_PROTOCOL = 0x00000004, + + // If set, only consider 4 of the 6 non-Dolby mixing outputs. Early + // versions of the Zelda UCode only had 4. + FOUR_MIXING_DESTS = 0x00000008, }; static const std::map UCODE_FLAGS = { @@ -32,8 +36,10 @@ static const std::map UCODE_FLAGS = { { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, - // GameCube IPL/BIOS. - { 0x6BA3B3EA, LIGHT_PROTOCOL }, + // GameCube IPL/BIOS, PAL. + { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, + // GameCube IPL/BIOS, NTSC. + { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // TODO: Other games that use this UCode (exhaustive list): // * Animal Crossing (type ????, CRC ????) @@ -973,44 +979,45 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) { // TODO: Store input samples if requested by the VPB. + int num_channels = (m_flags & FOUR_MIXING_DESTS) ? 4 : 6; if (vpb.end_requested) { bool all_mute = true; - for (auto& channel : vpb.channels) + for (int i = 0; i < num_channels; ++i) { - channel.target_volume = channel.current_volume / 2; - all_mute &= (channel.target_volume == 0); + vpb.channels[i].target_volume = vpb.channels[i].current_volume / 2; + all_mute &= (vpb.channels[i].target_volume == 0); } if (all_mute) vpb.done = true; } - for (auto& channel : vpb.channels) + for (int i = 0; i < num_channels; ++i) { - if (!channel.id) + if (!vpb.channels[i].id) continue; - s16 volume_delta = channel.target_volume - channel.current_volume; + s16 volume_delta = vpb.channels[i].target_volume - vpb.channels[i].current_volume; s32 volume_step = (volume_delta << 16) / (s32)input_samples.size(); // In 1.31 format. // TODO: The last value of each channel structure is used to // determine whether a channel should be skipped or not. Not // implemented yet. - if (!channel.current_volume && !volume_step) + if (!vpb.channels[i].current_volume && !volume_step) continue; - MixingBuffer* dst_buffer = BufferForID(channel.id); + MixingBuffer* dst_buffer = BufferForID(vpb.channels[i].id); if (!dst_buffer) { - PanicAlert("Mixing to an unmapped buffer: %04x", channel.id); + PanicAlert("Mixing to an unmapped buffer: %04x", vpb.channels[i].id); continue; } s32 new_volume = AddBuffersWithVolumeRamp( - dst_buffer, input_samples, channel.current_volume << 16, + dst_buffer, input_samples, vpb.channels[i].current_volume << 16, volume_step); - channel.current_volume = new_volume >> 16; + vpb.channels[i].current_volume = new_volume >> 16; } } From 43882f3e6eb262def5ecd0012931522039f56d45 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 22 Dec 2014 12:11:16 +0100 Subject: [PATCH 29/59] Zelda HLE: Handle the smaller VPBs used by the NTSC IPL. Damn you, Nintendo. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 91 +++++++++++++++++---- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 2 +- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8ad7e2871b..66d0080008 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -27,19 +27,23 @@ enum ZeldaUCodeFlag // If set, only consider 4 of the 6 non-Dolby mixing outputs. Early // versions of the Zelda UCode only had 4. FOUR_MIXING_DESTS = 0x00000008, + + // Handle smaller VPBs that are missing their 0x40-0x80 area. Very early + // versions of the Zelda UCode used 0x80 sized VPBs. + TINY_VPB = 0x00000010, }; static const std::map UCODE_FLAGS = { + // GameCube IPL/BIOS, NTSC. + { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | TINY_VPB }, + // GameCube IPL/BIOS, PAL. + { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Twilight Princess / GC. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, - // GameCube IPL/BIOS, PAL. - { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, - // GameCube IPL/BIOS, NTSC. - { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // TODO: Other games that use this UCode (exhaustive list): // * Animal Crossing (type ????, CRC ????) @@ -494,9 +498,6 @@ void ZeldaUCode::RenderAudio() #pragma pack(push, 1) struct ZeldaAudioRenderer::VPB { - static const u16 SIZE_IN_WORDS = 0xC0; - static const u16 RW_SIZE_IN_WORDS = 0x80; - // If zero, skip processing this voice. u16 enabled; @@ -700,7 +701,55 @@ struct ZeldaAudioRenderer::VPB u16 base_address_l; DEFINE_32BIT_ACCESSOR(base_address, BaseAddress) - u16 padding[SIZE_IN_WORDS]; + u16 padding[0xC0]; + + // These next two functions are terrible hacks used in order to support two + // different VPB sizes. + + // Transforms from an NTSC-IPL type 0x80-sized VPB to a full size VPB. + void Uncompress() + { + u16* words = (u16*)this; + // RO part of the VPB is from 0x40-0x80 instead of 0x80-0xC0. + for (int i = 0; i < 0x40; ++i) + { + words[0x80 + i] = words[0x40 + i]; + words[0x40 + i] = 0; + } + // AFC decoded samples are offset by 0x28. + for (int i = 0; i < 0x10; ++i) + { + words[0x58 + i] = words[0x30 + i]; + words[0x30 + i] = 0; + } + // Most things are offset by 0x18 because no Dolby mixing. + for (int i = 0; i < 0x18; ++i) + { + words[0x30 + i] = words[0x18 + i]; + words[0x18 + i] = 0; + } + } + + // Transforms from a full size VPB to an NTSC-IPL 0x80-sized VPB. + void Compress() + { + u16* words = (u16*)this; + for (int i = 0; i < 0x18; ++i) + { + words[0x18 + i] = words[0x30 + i]; + words[0x30 + i] = 0; + } + for (int i = 0; i < 0x10; ++i) + { + words[0x30 + i] = words[0x58 + i]; + words[0x58 + i] = 0; + } + for (int i = 0; i < 0x40; ++i) + { + words[0x40 + i] = words[0x80 + i]; + words[0x80 + i] = 0; + } + } }; struct ReverbPB @@ -1026,7 +1075,7 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) if (!vpb.use_constant_sample) vpb.reset_vpb = false; - StoreVPB(voice_id, vpb); + StoreVPB(voice_id, &vpb); } void ZeldaAudioRenderer::FinalizeFrame() @@ -1058,20 +1107,32 @@ void ZeldaAudioRenderer::FetchVPB(u16 voice_id, VPB* vpb) u16* vpb_words = (u16*)vpb; u16* ram_vpbs = (u16*)HLEMemory_Get_Pointer(m_vpb_base_addr); - size_t base_idx = voice_id * VPB::SIZE_IN_WORDS; - for (size_t i = 0; i < VPB::SIZE_IN_WORDS; ++i) + // A few versions of the UCode have VPB of size 0x80 (vs. the standard + // 0xC0). The whole 0x40-0x80 part is gone. Handle that by moving things + // around. + size_t vpb_size = (m_flags & TINY_VPB) ? 0x80 : 0xC0; + + size_t base_idx = voice_id * vpb_size; + for (size_t i = 0; i < vpb_size; ++i) vpb_words[i] = Common::swap16(ram_vpbs[base_idx + i]); + + if (m_flags & TINY_VPB) + vpb->Uncompress(); } -void ZeldaAudioRenderer::StoreVPB(u16 voice_id, const VPB& vpb) +void ZeldaAudioRenderer::StoreVPB(u16 voice_id, VPB* vpb) { - const u16* vpb_words = (const u16*)&vpb; + u16* vpb_words = (u16*)vpb; u16* ram_vpbs = (u16*)HLEMemory_Get_Pointer(m_vpb_base_addr); - size_t base_idx = voice_id * VPB::SIZE_IN_WORDS; + size_t vpb_size = (m_flags & TINY_VPB) ? 0x80 : 0xC0; + size_t base_idx = voice_id * vpb_size; + + if (m_flags & TINY_VPB) + vpb->Compress(); // Only the first 0x80 words are transferred back - the rest is read-only. - for (size_t i = 0; i < VPB::RW_SIZE_IN_WORDS; ++i) + for (size_t i = 0; i < vpb_size - 0x40; ++i) ram_vpbs[base_idx + i] = Common::swap16(vpb_words[i]); } diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index 7a3ad011a5..f5a8cfd96f 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -127,7 +127,7 @@ private: // Base address where VPBs are stored linearly in RAM. u32 m_vpb_base_addr; void FetchVPB(u16 voice_id, VPB* vpb); - void StoreVPB(u16 voice_id, const VPB& vpb); + void StoreVPB(u16 voice_id, VPB* vpb); // Sine table transferred from MRAM. Contains sin(x) values for x in // [0.0;pi/4] (sin(x) in [1.0;0.0]), in 1.15 fixed format. From 01ab40fc6e0db6a8ae77d9a63f8e4d1075aefd53 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 23 Dec 2014 19:14:29 +0100 Subject: [PATCH 30/59] Zelda HLE: NTSC IPL provides the volume stepping explicitly. Support that through a new behavior flag. Now the only remaining known bug in the NTSC IPL is the reverb not fading out. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 66d0080008..6774f1cfd9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -31,11 +31,16 @@ enum ZeldaUCodeFlag // Handle smaller VPBs that are missing their 0x40-0x80 area. Very early // versions of the Zelda UCode used 0x80 sized VPBs. TINY_VPB = 0x00000010, + + // If set, interpret non-Dolby mixing parameters as step/current volume + // instead of target/current volume. + VOLUME_EXPLICIT_STEP = 0x00000020, }; static const std::map UCODE_FLAGS = { // GameCube IPL/BIOS, NTSC. - { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | TINY_VPB }, + { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | TINY_VPB | + VOLUME_EXPLICIT_STEP }, // GameCube IPL/BIOS, PAL. { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // The Legend of Zelda: The Wind Waker. @@ -1046,7 +1051,14 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) if (!vpb.channels[i].id) continue; - s16 volume_delta = vpb.channels[i].target_volume - vpb.channels[i].current_volume; + // Some UCode versions provide the delta directly instead of + // providing a target volume. + s16 volume_delta; + if (m_flags & VOLUME_EXPLICIT_STEP) + volume_delta = (vpb.channels[i].target_volume << 16); + else + volume_delta = vpb.channels[i].target_volume - vpb.channels[i].current_volume; + s32 volume_step = (volume_delta << 16) / (s32)input_samples.size(); // In 1.31 format. // TODO: The last value of each channel structure is used to From a8810e8778e9cc59422bae55b3380367d69a8378 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 23 Dec 2014 19:39:10 +0100 Subject: [PATCH 31/59] Zelda HLE: Add support for the Zelda: FSA UCode. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index e0982ad3e8..c42380dd6c 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -57,6 +57,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0xd643001f: // Super Mario Galaxy - US case 0x6ba3b3ea: // GC IPL - PAL case 0x24b22038: // GC IPL - US + case 0x2fcdf1ec: // Zelda FSA - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 6774f1cfd9..b6715ad6f3 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -45,6 +45,8 @@ static const std::map UCODE_FLAGS = { { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, + // The Legend of Zelda: Four Swords Adventure. + { 0x2FCDF1EC, MAKE_DOLBY_LOUDER }, // The Legend of Zelda: Twilight Princess / GC. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. From 2e72c11025475fbe4622edca886b9d9a847c31f7 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 23 Dec 2014 21:12:35 +0100 Subject: [PATCH 32/59] Zelda HLE: Add support for the Pikmin 1 NTSC version of the UCode. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 4 ++++ Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 1 + 3 files changed, 6 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index c42380dd6c..d8e951ff74 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -58,6 +58,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x6ba3b3ea: // GC IPL - PAL case 0x24b22038: // GC IPL - US case 0x2fcdf1ec: // Zelda FSA - US + case 0x4be6a5cb: // Pikmin 1 GC - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index b6715ad6f3..4cff8f123f 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -43,6 +43,8 @@ static const std::map UCODE_FLAGS = { VOLUME_EXPLICIT_STEP }, // GameCube IPL/BIOS, PAL. { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, + // Pikmin 1 GC NTSC. + { 0x4BE6A5CB, LIGHT_PROTOCOL }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Four Swords Adventure. @@ -945,6 +947,7 @@ ZeldaAudioRenderer::MixingBuffer* ZeldaAudioRenderer::BufferForID(u16 buffer_id) case 0x0E20: return &m_buf_unk1_reverb; case 0x09A0: return &m_buf_unk0; // Used by the GC IPL as a reverb dest. case 0x0FA0: return &m_buf_unk1; // Used by the GC IPL as a mixing dest. + case 0x0B00: return &m_buf_unk2; // Used by Pikmin 1 as a mixing dest. default: return nullptr; } } @@ -1592,6 +1595,7 @@ void ZeldaAudioRenderer::DoState(PointerWrap& p) p.Do(m_buf_unk1_reverb); p.Do(m_buf_unk0); p.Do(m_buf_unk1); + p.Do(m_buf_unk2); p.Do(m_resampling_coeffs); p.Do(m_const_patterns); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index f5a8cfd96f..fbf2729828 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -119,6 +119,7 @@ private: MixingBuffer m_buf_unk1_reverb{}; MixingBuffer m_buf_unk0{}; MixingBuffer m_buf_unk1{}; + MixingBuffer m_buf_unk2{}; // Maps a buffer "ID" (really, their address in the DSP DRAM...) to our // buffers. Returns nullptr if no match is found. From a0c318454ddad264b7c79495af3c202a80c710ac Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 23 Dec 2014 21:29:18 +0100 Subject: [PATCH 33/59] Zelda HLE: Log the UCode version being used (CRC/flags). Also tidy up the versions list a bit. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 4cff8f123f..bc188e5525 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -44,26 +44,24 @@ static const std::map UCODE_FLAGS = { // GameCube IPL/BIOS, PAL. { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // Pikmin 1 GC NTSC. + // Anilam Crossing. { 0x4BE6A5CB, LIGHT_PROTOCOL }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, - // The Legend of Zelda: Four Swords Adventure. + // The Legend of Zelda: Four Swords Adventures. { 0x2FCDF1EC, MAKE_DOLBY_LOUDER }, // The Legend of Zelda: Twilight Princess / GC. + // Donkey Kong Jungle Beat. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, // TODO: Other games that use this UCode (exhaustive list): - // * Animal Crossing (type ????, CRC ????) - // * Donkey Kong Jungle Beat (type ????, CRC ????) // * Luigi's Mansion (type ????, CRC ????) // * Mario Kart: Double Dash!! (type ????, CRC ????) - // * Pikmin (type ????, CRC ????) // * Pikmin 2 (type ????, CRC ????) // * Super Mario Galaxy 2 (type ????, CRC ????) // * Super Mario Sunshine (type ????, CRC ????) - // * The Legend of Zelda: Four Swords Adventures (type ????, CRC ????) // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) }; @@ -77,6 +75,8 @@ ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) m_flags = it->second; m_renderer.SetFlags(m_flags); + WARN_LOG(DSPHLE, "Zelda UCode loaded, crc=%08x, flags=%08x", crc, m_flags); + if (m_flags & LIGHT_PROTOCOL) { m_mail_handler.PushMail(0x88881111); From 3c2b22bc09b8bdfedd572618f96b68ad9566a718 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 23 Dec 2014 21:39:57 +0100 Subject: [PATCH 34/59] Zelda HLE: Add basic support for Luigi's Mansion. Fails ingame because it mixes to some buffers that are considered the back buffers for Dolby games. That check might need to be less restrictive for games that don't use Dolby mixing. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index d8e951ff74..0cf1d6fd5a 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -59,6 +59,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x24b22038: // GC IPL - US case 0x2fcdf1ec: // Zelda FSA - US case 0x4be6a5cb: // Pikmin 1 GC - US + case 0x42f64ac4: // Luigi's Mansion - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index bc188e5525..258a193568 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -44,8 +44,10 @@ static const std::map UCODE_FLAGS = { // GameCube IPL/BIOS, PAL. { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, // Pikmin 1 GC NTSC. - // Anilam Crossing. + // Animal Crossing. { 0x4BE6A5CB, LIGHT_PROTOCOL }, + // Luigi's Mansion. + { 0x42F64AC4, LIGHT_PROTOCOL }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Four Swords Adventures. @@ -57,7 +59,6 @@ static const std::map UCODE_FLAGS = { { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, // TODO: Other games that use this UCode (exhaustive list): - // * Luigi's Mansion (type ????, CRC ????) // * Mario Kart: Double Dash!! (type ????, CRC ????) // * Pikmin 2 (type ????, CRC ????) // * Super Mario Galaxy 2 (type ????, CRC ????) From b3a327f02b65baa0a631b4b67dcf5ab00ce14c32 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 24 Dec 2014 00:55:56 +0100 Subject: [PATCH 35/59] Zelda HLE: Implement sample source 3. "Square" wave at a 0.25 duty cycle (25% up 75% down). --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 258a193568..a7fc1b38b9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -59,10 +59,12 @@ static const std::map UCODE_FLAGS = { { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, // TODO: Other games that use this UCode (exhaustive list): + // * Link's Crossbow Training // * Mario Kart: Double Dash!! (type ????, CRC ????) // * Pikmin 2 (type ????, CRC ????) // * Super Mario Galaxy 2 (type ????, CRC ????) // * Super Mario Sunshine (type ????, CRC ????) + // * The Legend of Zelda: Collector's Edition // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) }; @@ -652,6 +654,9 @@ struct ZeldaAudioRenderer::VPB // Simple saw wave at 100% amplitude and frequency controlled via the // resampling ratio. SRC_SAW_WAVE = 1, + // Same "square" wave as SRC_SQUARE_WAVE but using a 0.25 duty cycle + // instead of 0.5. + SRC_SQUARE_WAVE_25PCT = 3, // Breaking the numerical ordering for these, but they are all related. // Simple pattern stored in the data downloaded by command 01. Playback // frequency is controlled by the resampling ratio. @@ -1175,14 +1180,22 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) switch (vpb->samples_source_type) { case VPB::SRC_SQUARE_WAVE: + case VPB::SRC_SQUARE_WAVE_25PCT: { - u32 pos = vpb->current_pos_frac << 1; + u32 shift; + if (vpb->samples_source_type == VPB::SRC_SQUARE_WAVE) + shift = 1; + else + shift = 2; + u32 mask = (1 << shift) - 1; + + u32 pos = vpb->current_pos_frac << shift; for (size_t i = 0; i < buffer->size(); ++i) { - (*buffer)[i] = ((pos >> 16) & 1) ? 0x4000 : 0xC000; + (*buffer)[i] = ((pos >> 16) & mask) ? 0xC000 : 0x4000; pos += vpb->resampling_ratio; } - vpb->current_pos_frac = (pos >> 1) & 0xFFFF; + vpb->current_pos_frac = (pos >> shift) & 0xFFFF; break; } From 82689677d952a139d2abd1d5f58cedd9e65efe25 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 24 Dec 2014 03:29:26 +0100 Subject: [PATCH 36/59] Zelda HLE: Implement LQ AFC decoding (samples source 0005). --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index a7fc1b38b9..9a742bc5f2 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -664,6 +664,9 @@ struct ZeldaAudioRenderer::VPB SRC_CONST_PATTERN_1 = 4, SRC_CONST_PATTERN_2 = 11, SRC_CONST_PATTERN_3 = 12, + // Samples stored in ARAM at a rate of 16 samples/5 bytes, AFC encoded, + // at an arbitrary sample rate (resampling is applied). + SRC_AFC_LQ_FROM_ARAM = 5, // Samples stored in ARAM in PCM8 format, at an arbitrary sampling rate // (resampling is applied). SRC_PCM8_FROM_ARAM = 8, @@ -1248,6 +1251,7 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) break; case VPB::SRC_AFC_HQ_FROM_ARAM: + case VPB::SRC_AFC_LQ_FROM_ARAM: DownloadAFCSamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); Resample(vpb, raw_input_samples.data(), buffer); @@ -1520,13 +1524,25 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) for (auto& nibble : nibbles) { if (nibble >= 8) - nibble = nibble - 16; + nibble -= 16; nibble <<= 11; } } else { - // TODO: LQ samples. + for (size_t i = 0; i < 16; i += 4) + { + nibbles[i + 0] = (*src >> 6) & 3; + nibbles[i + 1] = (*src >> 4) & 3; + nibbles[i + 2] = (*src >> 2) & 3; + nibbles[i + 3] = (*src >> 6) & 3; + } + for (auto& nibble : nibbles) + { + if (nibble >= 2) + nibble -= 4; + nibble <<= 13; + } } s32 yn1 = *vpb->AFCYN1(), yn2 = *vpb->AFCYN2(); From 840a4157b07f2e327e4aedf1c2116acf9ede1e05 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 24 Dec 2014 04:26:05 +0100 Subject: [PATCH 37/59] Zelda HLE: Properly implement LQ AFC decoding. Typos and stupid mistakes + untested code = dirty Git history. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 9a742bc5f2..1138743e73 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1535,7 +1535,8 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count) nibbles[i + 0] = (*src >> 6) & 3; nibbles[i + 1] = (*src >> 4) & 3; nibbles[i + 2] = (*src >> 2) & 3; - nibbles[i + 3] = (*src >> 6) & 3; + nibbles[i + 3] = (*src >> 0) & 3; + src++; } for (auto& nibble : nibbles) { From 837b8041c00f18c04e3c7c1a3197b975acdd2cc5 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 24 Dec 2014 04:29:13 +0100 Subject: [PATCH 38/59] Zelda HLE: Only alert for mixing into back buffers for recent UCode versions. On older versions there is no such concept of a "back buffer" since positional audio is not supported. These buffers are just used for temp mixing, and while it would be really nice if we could support more of that inter-buffer mixing (TODO :) ) it does not warrant a PanicAlert at this time. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 1138743e73..35c27ff987 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -808,7 +808,7 @@ void ZeldaAudioRenderer::PrepareFrame() // TODO: Back left and back right should have a filter applied to them, // then get mixed into front left and front right. In practice, TWW never // uses this AFAICT. PanicAlert to help me find places that use this. - if (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0) + if (!(m_flags & LIGHT_PROTOCOL) && (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0)) PanicAlert("Zelda HLE using back mixing buffers"); // Add reverb data from previous frame. From 89037781e01bbc4f2ccb991f0f7506ca10f07d66 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 26 Dec 2014 19:17:35 +0100 Subject: [PATCH 39/59] Zelda HLE: Make the implementation less strict for recoverable errors. It kind of sucks that we don't emulate some behaviors properly, but there is very little ROI for some of these features and I'm not going to spend time implementing them any time soon. Making the PanicAlerts optional allows for more testing of the core features on more games while "just" breaking less important features like reverb. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 35c27ff987..16823c8bbf 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -7,6 +7,11 @@ #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/DSPHLE/UCodes/Zelda.h" +// Uncomment this to have a strict version of the HLE implementation, which +// PanicAlerts on recoverable unknown behaviors instead of silently ignoring +// them. Recommended for development. +// #define STRICT_ZELDA_HLE 1 + // These flags modify the behavior of the HLE implementation based on the UCode // version. When introducing a new flag, please recheck the behavior of each // UCode version. @@ -808,8 +813,10 @@ void ZeldaAudioRenderer::PrepareFrame() // TODO: Back left and back right should have a filter applied to them, // then get mixed into front left and front right. In practice, TWW never // uses this AFAICT. PanicAlert to help me find places that use this. +#ifdef STRICT_ZELDA_HLE if (!(m_flags & LIGHT_PROTOCOL) && (m_buf_back_left[0] != 0 || m_buf_back_right[0] != 0)) PanicAlert("Zelda HLE using back mixing buffers"); +#endif // Add reverb data from previous frame. ApplyReverb(false); @@ -837,7 +844,9 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) { if (!m_reverb_pb_base_addr) { +#ifdef STRICT_ZELDA_HLE PanicAlert("Trying to apply reverb without available parameters."); +#endif return; } @@ -912,7 +921,9 @@ void ZeldaAudioRenderer::ApplyReverb(bool post_rendering) MixingBuffer* dest_buffer = BufferForID(dest.buffer_id); if (!dest_buffer) { +#ifdef STRICT_ZELDA_HLE PanicAlert("RPB mixing to an unknown buffer: %04x", dest.buffer_id); +#endif continue; } AddBuffersWithVolume(dest_buffer->data(), buffer.data(), @@ -1085,7 +1096,9 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id) MixingBuffer* dst_buffer = BufferForID(vpb.channels[i].id); if (!dst_buffer) { +#ifdef STRICT_ZELDA_HLE PanicAlert("Mixing to an unmapped buffer: %04x", vpb.channels[i].id); +#endif continue; } From addb5cb8870203e90b339c23846bf97d91c3b8a6 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 27 Dec 2014 12:15:27 +0100 Subject: [PATCH 40/59] Zelda HLE: Support the per-frame sync protocol used by SMS. Didn't test if SMS sounds right (travelling with no headphones \o/) but the waveform looks ok and the mails are flowing as expected. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 32 +++++++++++++++++--- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h | 3 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index 0cf1d6fd5a..f52d03c495 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -60,6 +60,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x2fcdf1ec: // Zelda FSA - US case 0x4be6a5cb: // Pikmin 1 GC - US case 0x42f64ac4: // Luigi's Mansion - US + case 0x56d36052: // Super Mario Sunshine - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 16823c8bbf..fd1d044ca1 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -40,6 +40,9 @@ enum ZeldaUCodeFlag // If set, interpret non-Dolby mixing parameters as step/current volume // instead of target/current volume. VOLUME_EXPLICIT_STEP = 0x00000020, + + // If set, handle synchronization per-frame instead of per-16-voices. + SYNC_PER_FRAME = 0x00000040, }; static const std::map UCODE_FLAGS = { @@ -53,6 +56,8 @@ static const std::map UCODE_FLAGS = { { 0x4BE6A5CB, LIGHT_PROTOCOL }, // Luigi's Mansion. { 0x42F64AC4, LIGHT_PROTOCOL }, + // Super Mario Sunshine. + { 0x56D36052, SYNC_PER_FRAME }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Four Swords Adventures. @@ -122,6 +127,7 @@ void ZeldaUCode::DoState(PointerWrap &p) p.Do(m_sync_max_voice_id); p.Do(m_sync_voice_skip_flags); + p.Do(m_sync_flags_second_half); p.Do(m_cmd_buffer); p.Do(m_read_offset); @@ -214,11 +220,27 @@ void ZeldaUCode::HandleMailDefault(u32 mail) break; case MailState::RENDERING: - m_sync_max_voice_id = (((mail >> 16) & 0xF) + 1) << 4; - m_sync_voice_skip_flags[(mail >> 16) & 0xFF] = mail & 0xFFFF; + if (m_flags & SYNC_PER_FRAME) + { + int base = m_sync_flags_second_half ? 2 : 0; + m_sync_voice_skip_flags[base] = mail >> 16; + m_sync_voice_skip_flags[base + 1] = mail & 0xFFFF; - RenderAudio(); - SetMailState(MailState::WAITING); + if (m_sync_flags_second_half) + m_sync_max_voice_id = 0xFFFF; + + RenderAudio(); + if (m_sync_flags_second_half) + SetMailState(MailState::WAITING); + m_sync_flags_second_half = !m_sync_flags_second_half; + } + else + { + m_sync_max_voice_id = (((mail >> 16) & 0xF) + 1) << 4; + m_sync_voice_skip_flags[(mail >> 16) & 0xFF] = mail & 0xFFFF; + RenderAudio(); + SetMailState(MailState::WAITING); + } break; case MailState::WRITING_CMD: @@ -286,7 +308,7 @@ void ZeldaUCode::HandleMailLight(u32 mail) // No per-voice syncing in the light protocol. m_sync_max_voice_id = 0xFFFFFFFF; - m_sync_voice_skip_flags.fill(0xFFFFFFFF); + m_sync_voice_skip_flags.fill(0xFFFF); RenderAudio(); DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP); break; diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h index fbf2729828..8f62aaa376 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.h @@ -236,7 +236,8 @@ private: // these sync mails contain 16 bit values that are used as bitfields to // control voice skipping on a voice per voice level. u32 m_sync_max_voice_id = 0; - std::array m_sync_voice_skip_flags{}; + std::array m_sync_voice_skip_flags{}; + bool m_sync_flags_second_half = false; // Command buffer (circular queue with r/w indices). Filled by HandleMail // when the state machine is in WRITING_CMD state. Commands get executed From 5f61ab3e21b4145a4e34679203b93d0f01870055 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 27 Dec 2014 14:41:10 +0100 Subject: [PATCH 41/59] Zelda HLE: Update the CRC to games mapping (documentation only). --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index fd1d044ca1..c301206875 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -51,29 +51,28 @@ static const std::map UCODE_FLAGS = { VOLUME_EXPLICIT_STEP }, // GameCube IPL/BIOS, PAL. { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, + // Luigi's Mansion. + { 0x42F64AC4, LIGHT_PROTOCOL }, // Pikmin 1 GC NTSC. // Animal Crossing. { 0x4BE6A5CB, LIGHT_PROTOCOL }, - // Luigi's Mansion. - { 0x42F64AC4, LIGHT_PROTOCOL }, // Super Mario Sunshine. { 0x56D36052, SYNC_PER_FRAME }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Four Swords Adventures. + // Mario Kart: Double Dash. + // Pikmin 2 GC NTSC. { 0x2FCDF1EC, MAKE_DOLBY_LOUDER }, // The Legend of Zelda: Twilight Princess / GC. // Donkey Kong Jungle Beat. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. + // Super Mario Galaxy 2. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, // TODO: Other games that use this UCode (exhaustive list): // * Link's Crossbow Training - // * Mario Kart: Double Dash!! (type ????, CRC ????) - // * Pikmin 2 (type ????, CRC ????) - // * Super Mario Galaxy 2 (type ????, CRC ????) - // * Super Mario Sunshine (type ????, CRC ????) // * The Legend of Zelda: Collector's Edition // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) }; From cdb102269600efc8aaddcdebee34d38335882fe9 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 27 Dec 2014 20:33:34 +0100 Subject: [PATCH 42/59] Zelda HLE: Add support for the ZTP Wii UCode. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index f52d03c495..517a6fe190 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -61,6 +61,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x4be6a5cb: // Pikmin 1 GC - US case 0x42f64ac4: // Luigi's Mansion - US case 0x56d36052: // Super Mario Sunshine - US + case 0x6c3f6f94: // Zelda TP Wii - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index c301206875..4852143b9b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -67,6 +67,8 @@ static const std::map UCODE_FLAGS = { // The Legend of Zelda: Twilight Princess / GC. // Donkey Kong Jungle Beat. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, + // The Legend of Zelda: Twilight Princess / Wii. + { 0x6C3F6F94, NO_ARAM | MAKE_DOLBY_LOUDER }, // Super Mario Galaxy. // Super Mario Galaxy 2. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, @@ -1676,5 +1678,4 @@ void ZeldaAudioRenderer::DoState(PointerWrap& p) p.Do(m_buf_unk1_reverb_last8); p.Do(m_buf_front_left_reverb_last8); p.Do(m_buf_front_right_reverb_last8); - } From 7f7e036a029643df4678f3fad7d6fdec4b43e57f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 18:30:48 +0200 Subject: [PATCH 43/59] ZeldaHLE: Add a missing game to the list of games to look at. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 4852143b9b..c994638161 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -77,6 +77,7 @@ static const std::map UCODE_FLAGS = { // * Link's Crossbow Training // * The Legend of Zelda: Collector's Edition // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) + // * Pikmin 1/2 New Play Control! }; ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) From 8526a4131cbe61de26001a508282691cf92fd37a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 18:31:02 +0200 Subject: [PATCH 44/59] ZeldaHLE: Properly implement light command 03. On at least one version (AC/Pikmin 1 NTSC) it doesn't even send a sync mail and just reloops to the dispatcher. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index c994638161..4f3fbdd4fb 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -264,6 +264,8 @@ void ZeldaUCode::HandleMailDefault(u32 mail) void ZeldaUCode::HandleMailLight(u32 mail) { + bool add_command = true; + switch (m_mail_current_state) { case MailState::WAITING: @@ -274,11 +276,15 @@ void ZeldaUCode::HandleMailLight(u32 mail) // for each command - the alternative is to rewrite command handling as // an asynchronous procedure, and we wouldn't want that, would we? Write32(mail); + switch ((mail >> 24) & 0x7F) { case 0: m_mail_expected_cmd_mails = 0; break; case 1: m_mail_expected_cmd_mails = 4; break; case 2: m_mail_expected_cmd_mails = 2; break; + // Doesn't even register as a command, just rejumps to the dispatcher. + case 3: add_command = false; break; + default: PanicAlert("Received unknown command in light protocol: %08x", mail); break; @@ -287,7 +293,7 @@ void ZeldaUCode::HandleMailLight(u32 mail) { SetMailState(MailState::WRITING_CMD); } - else + else if (add_command) { m_pending_commands_count += 1; RunPendingCommands(); From 25430e792309799d18cd6c0f4306cac66d00e571 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 18:34:31 +0200 Subject: [PATCH 45/59] ZeldaHLE: Add a flag to disable Cmd0D on older UCodes. Shouldn't really matter -- these NOP commands aren't really used, but since it reads an argument it's probably better to explicitly NOP it for safety. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 24 ++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 4f3fbdd4fb..fba63216ed 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -43,20 +43,26 @@ enum ZeldaUCodeFlag // If set, handle synchronization per-frame instead of per-16-voices. SYNC_PER_FRAME = 0x00000040, + + // If set, does not support command 0D. TODO: rename. + NO_CMD_0D = 0x00000080, }; static const std::map UCODE_FLAGS = { // GameCube IPL/BIOS, NTSC. + // TODO: check NO_CMD_0D presence. { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | TINY_VPB | - VOLUME_EXPLICIT_STEP }, + VOLUME_EXPLICIT_STEP | NO_CMD_0D }, // GameCube IPL/BIOS, PAL. - { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS }, - // Luigi's Mansion. - { 0x42F64AC4, LIGHT_PROTOCOL }, + // TODO: check NO_CMD_0D presence. + { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | NO_CMD_0D }, // Pikmin 1 GC NTSC. // Animal Crossing. - { 0x4BE6A5CB, LIGHT_PROTOCOL }, + { 0x4BE6A5CB, LIGHT_PROTOCOL | NO_CMD_0D }, + // Luigi's Mansion. + { 0x42F64AC4, LIGHT_PROTOCOL }, // Super Mario Sunshine. + // TODO: check NO_CMD_0D presence. { 0x56D36052, SYNC_PER_FRAME }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, @@ -283,6 +289,7 @@ void ZeldaUCode::HandleMailLight(u32 mail) case 1: m_mail_expected_cmd_mails = 4; break; case 2: m_mail_expected_cmd_mails = 2; break; // Doesn't even register as a command, just rejumps to the dispatcher. + // TODO: That's true on 0x4BE6A5CB and 0x42F64AC4, what about others? case 3: add_command = false; break; default: @@ -436,6 +443,13 @@ void ZeldaUCode::RunPendingCommands() // Command 0D: TODO: find a name and implement. case 0x0D: + if (m_flags & NO_CMD_0D) + { + WARN_LOG(DSPHLE, "Received a 0D command which is NOP'd on this UCode."); + SendCommandAck(CommandAck::STANDARD, sync); + break; + } + WARN_LOG(DSPHLE, "CMD0D: %08x", Read32()); SendCommandAck(CommandAck::STANDARD, sync); break; From bbfa238657e46fee282b7dc28f5715d49f178450 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 19:50:28 +0200 Subject: [PATCH 46/59] ZeldaHLE: Document that the crashy commands are not actually crashy on light protocol, just never used. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index fba63216ed..8aef08b7c0 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -375,7 +375,11 @@ void ZeldaUCode::RunPendingCommands() case 0x07: case 0x08: case 0x09: - // Commands that crash the DAC UCode. Log and enter HALTED mode. + // Commands that crash the DAC UCode on non-light protocols. Log and + // enter HALTED mode. + // + // TODO: These are not crashes on light protocol, however I've never seen + // them used so far. NOTICE_LOG(DSPHLE, "Received a crashy command: %d", command); SetMailState(MailState::HALTED); return; From 32fcd8ec7c9114054f636226269c37b21dd524b0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 19:59:47 +0200 Subject: [PATCH 47/59] ZeldaHLE: Special case Cmd03 NOP handling to make it clear it shouldn't ever happen on light protocol UCodes. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8aef08b7c0..8cf51b8788 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -358,7 +358,6 @@ void ZeldaUCode::RunPendingCommands() switch (command) { case 0x00: - case 0x03: case 0x0A: case 0x0B: case 0x0C: @@ -369,6 +368,17 @@ void ZeldaUCode::RunPendingCommands() SendCommandAck(CommandAck::STANDARD, sync); break; + case 0x03: + // NOP on standard protocol but shouldn't ever happen on light protocol + // since it's going directly back to the dispatcher with no ack. + if (m_flags & LIGHT_PROTOCOL) + { + PanicAlert("Received a 03 command on light protocol."); + break; + } + SendCommandAck(CommandAck::STANDARD, sync); + break; + case 0x04: case 0x05: case 0x06: From 0b1af503162699915142d3d174b6040b95cb86bd Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 21:12:44 +0200 Subject: [PATCH 48/59] ZeldaHLE: Initial support for command 0C in both styles implemented by the light protocol. Logs, but doesn't crash anymore. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 60 ++++++++++++++++----- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8cf51b8788..8700264825 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -46,24 +46,29 @@ enum ZeldaUCodeFlag // If set, does not support command 0D. TODO: rename. NO_CMD_0D = 0x00000080, + + // If set, command 0C is used for GBA crypto. This was used before the GBA + // UCode and UCode switching was available. + SUPPORTS_GBA_CRYPTO = 0x00000100, + + // If set, command 0C is used for an unknown purpose. TODO: rename. + WEIRD_CMD_0C = 0x00000200, }; static const std::map UCODE_FLAGS = { // GameCube IPL/BIOS, NTSC. - // TODO: check NO_CMD_0D presence. { 0x24B22038, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | TINY_VPB | - VOLUME_EXPLICIT_STEP | NO_CMD_0D }, + VOLUME_EXPLICIT_STEP | NO_CMD_0D | WEIRD_CMD_0C }, // GameCube IPL/BIOS, PAL. - // TODO: check NO_CMD_0D presence. - { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | NO_CMD_0D }, + { 0x6BA3B3EA, LIGHT_PROTOCOL | FOUR_MIXING_DESTS | NO_CMD_0D | + WEIRD_CMD_0C }, // Pikmin 1 GC NTSC. // Animal Crossing. - { 0x4BE6A5CB, LIGHT_PROTOCOL | NO_CMD_0D }, + { 0x4BE6A5CB, LIGHT_PROTOCOL | NO_CMD_0D | SUPPORTS_GBA_CRYPTO }, // Luigi's Mansion. - { 0x42F64AC4, LIGHT_PROTOCOL }, + { 0x42F64AC4, LIGHT_PROTOCOL | NO_CMD_0D | WEIRD_CMD_0C }, // Super Mario Sunshine. - // TODO: check NO_CMD_0D presence. - { 0x56D36052, SYNC_PER_FRAME }, + { 0x56D36052, SYNC_PER_FRAME | NO_CMD_0D }, // The Legend of Zelda: The Wind Waker. { 0x86840740, 0 }, // The Legend of Zelda: Four Swords Adventures. @@ -285,12 +290,20 @@ void ZeldaUCode::HandleMailLight(u32 mail) switch ((mail >> 24) & 0x7F) { - case 0: m_mail_expected_cmd_mails = 0; break; - case 1: m_mail_expected_cmd_mails = 4; break; - case 2: m_mail_expected_cmd_mails = 2; break; + case 0x00: m_mail_expected_cmd_mails = 0; break; + case 0x01: m_mail_expected_cmd_mails = 4; break; + case 0x02: m_mail_expected_cmd_mails = 2; break; // Doesn't even register as a command, just rejumps to the dispatcher. // TODO: That's true on 0x4BE6A5CB and 0x42F64AC4, what about others? - case 3: add_command = false; break; + case 0x03: add_command = false; break; + case 0x0C: + if (m_flags & SUPPORTS_GBA_CRYPTO) + m_mail_expected_cmd_mails = 1; + else if (m_flags & WEIRD_CMD_0C) + m_mail_expected_cmd_mails = 2; + else + m_mail_expected_cmd_mails = 0; + break; default: PanicAlert("Received unknown command in light protocol: %08x", mail); @@ -360,7 +373,6 @@ void ZeldaUCode::RunPendingCommands() case 0x00: case 0x0A: case 0x0B: - case 0x0C: case 0x0F: // NOP commands. Log anyway in case we encounter a new version // where these are not NOPs anymore. @@ -455,6 +467,28 @@ void ZeldaUCode::RunPendingCommands() } return; + // Command 0C: used for multiple purpose depending on the UCode version: + // * IPL NTSC/PAL, Luigi's Mansion: TODO (unknown as of now). + // * Pikmin/AC: GBA crypto. + // * SMS and onwards: NOP. + case 0x0C: + if (m_flags & SUPPORTS_GBA_CRYPTO) + { + // TODO + NOTICE_LOG(DSPHLE, "Received an unhandled GBA crypto command, param: %08x", Read32()); + } + else if (m_flags & WEIRD_CMD_0C) + { + // TODO + NOTICE_LOG(DSPHLE, "Received an unhandled 0C command, params: %08x %08x", Read32(), Read32()); + } + else + { + WARN_LOG(DSPHLE, "Received a NOP 0C command. Flags=%08x", m_flags); + } + SendCommandAck(CommandAck::STANDARD, sync); + break; + // Command 0D: TODO: find a name and implement. case 0x0D: if (m_flags & NO_CMD_0D) From fbe727b0bb1645940c983449ed812012b66f225e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Aug 2015 21:21:42 +0200 Subject: [PATCH 49/59] ZeldaHLE: Support GBA crypto through command 0C. Reuses the pre-existing GBA crypto code from the separate UCode. --- Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp | 148 ++++++++++---------- Source/Core/Core/HW/DSPHLE/UCodes/GBA.h | 5 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 4 +- 3 files changed, 83 insertions(+), 74 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp index a15f6e2af1..1c6d50674f 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp @@ -7,6 +7,81 @@ #include "Core/HW/DSPHLE/UCodes/GBA.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" +void ProcessGBACrypto(u32 address) +{ + struct sec_params_t + { + u16 key[2]; + u16 unk1[2]; + u16 unk2[2]; + u32 length; + u32 dest_addr; + u32 pad[3]; + } sec_params; + + // 32 bytes from mram addr to DRAM @ 0 + for (int i = 0; i < 8; i++, address += 4) + ((u32*)&sec_params)[i] = HLEMemory_Read_U32(address); + + // This is the main decrypt routine + u16 x11 = 0, x12 = 0, + x20 = 0, x21 = 0, x22 = 0, x23 = 0; + + x20 = Common::swap16(sec_params.key[0]) ^ 0x6f64; + x21 = Common::swap16(sec_params.key[1]) ^ 0x6573; + + s16 unk2 = (s8)sec_params.unk2[0]; + if (unk2 < 0) + { + x11 = ((~unk2 + 3) << 1) | (sec_params.unk1[0] << 4); + } + else if (unk2 == 0) + { + x11 = (sec_params.unk1[0] << 1) | 0x70; + } + else // unk2 > 0 + { + x11 = ((unk2 - 1) << 1) | (sec_params.unk1[0] << 4); + } + + s32 rounded_sub = ((sec_params.length + 7) & ~7) - 0x200; + u16 size = (rounded_sub < 0) ? 0 : rounded_sub >> 3; + + u32 t = (((size << 16) | 0x3f80) & 0x3f80ffff) << 1; + s16 t_low = (s8)(t >> 8); + t += (t_low & size) << 16; + x12 = t >> 16; + x11 |= (size & 0x4000) >> 14; // this would be stored in ac0.h if we weren't constrained to 32bit :) + t = ((x11 & 0xff) << 16) + ((x12 & 0xff) << 16) + (x12 << 8); + + u16 final11 = 0, final12 = 0; + final11 = x11 | ((t >> 8) & 0xff00) | 0x8080; + final12 = x12 | 0x8080; + + if ((final12 & 0x200) != 0) + { + x22 = final11 ^ 0x6f64; + x23 = final12 ^ 0x6573; + } + else + { + x22 = final11 ^ 0x6177; + x23 = final12 ^ 0x614b; + } + + // Send the result back to mram + *(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr) = Common::swap32((x20 << 16) | x21); + *(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr+4) = Common::swap32((x22 << 16) | x23); + + // Done! + DEBUG_LOG(DSPHLE, "\n%08x -> key: %08x, len: %08x, dest_addr: %08x, unk1: %08x, unk2: %08x" + " 22: %04x, 23: %04x", + address, + *(u32*)sec_params.key, sec_params.length, sec_params.dest_addr, + *(u32*)sec_params.unk1, *(u32*)sec_params.unk2, + x22, x23); +} + GBAUCode::GBAUCode(DSPHLE *dsphle, u32 crc) : UCodeInterface(dsphle, crc) { @@ -48,79 +123,8 @@ void GBAUCode::HandleMail(u32 mail) else if (nextmail_is_mramaddr) { nextmail_is_mramaddr = false; - u32 mramaddr = mail; - struct sec_params_t - { - u16 key[2]; - u16 unk1[2]; - u16 unk2[2]; - u32 length; - u32 dest_addr; - u32 pad[3]; - } sec_params; - - // 32 bytes from mram addr to DRAM @ 0 - for (int i = 0; i < 8; i++, mramaddr += 4) - ((u32*)&sec_params)[i] = HLEMemory_Read_U32(mramaddr); - - // This is the main decrypt routine - u16 x11 = 0, x12 = 0, - x20 = 0, x21 = 0, x22 = 0, x23 = 0; - - x20 = Common::swap16(sec_params.key[0]) ^ 0x6f64; - x21 = Common::swap16(sec_params.key[1]) ^ 0x6573; - - s16 unk2 = (s8)sec_params.unk2[0]; - if (unk2 < 0) - { - x11 = ((~unk2 + 3) << 1) | (sec_params.unk1[0] << 4); - } - else if (unk2 == 0) - { - x11 = (sec_params.unk1[0] << 1) | 0x70; - } - else // unk2 > 0 - { - x11 = ((unk2 - 1) << 1) | (sec_params.unk1[0] << 4); - } - - s32 rounded_sub = ((sec_params.length + 7) & ~7) - 0x200; - u16 size = (rounded_sub < 0) ? 0 : rounded_sub >> 3; - - u32 t = (((size << 16) | 0x3f80) & 0x3f80ffff) << 1; - s16 t_low = (s8)(t >> 8); - t += (t_low & size) << 16; - x12 = t >> 16; - x11 |= (size & 0x4000) >> 14; // this would be stored in ac0.h if we weren't constrained to 32bit :) - t = ((x11 & 0xff) << 16) + ((x12 & 0xff) << 16) + (x12 << 8); - - u16 final11 = 0, final12 = 0; - final11 = x11 | ((t >> 8) & 0xff00) | 0x8080; - final12 = x12 | 0x8080; - - if ((final12 & 0x200) != 0) - { - x22 = final11 ^ 0x6f64; - x23 = final12 ^ 0x6573; - } - else - { - x22 = final11 ^ 0x6177; - x23 = final12 ^ 0x614b; - } - - // Send the result back to mram - *(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr) = Common::swap32((x20 << 16) | x21); - *(u32*)HLEMemory_Get_Pointer(sec_params.dest_addr+4) = Common::swap32((x22 << 16) | x23); - - // Done! - DEBUG_LOG(DSPHLE, "\n%08x -> key: %08x, len: %08x, dest_addr: %08x, unk1: %08x, unk2: %08x" - " 22: %04x, 23: %04x", - mramaddr, - *(u32*)sec_params.key, sec_params.length, sec_params.dest_addr, - *(u32*)sec_params.unk1, *(u32*)sec_params.unk2, - x22, x23); + ProcessGBACrypto(mail); calc_done = true; m_mail_handler.PushMail(DSP_DONE); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/GBA.h b/Source/Core/Core/HW/DSPHLE/UCodes/GBA.h index 50530134bf..751750cce1 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/GBA.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/GBA.h @@ -6,6 +6,11 @@ #include "Core/HW/DSPHLE/UCodes/UCodes.h" +// Computes two 32 bit integers to be returned to the game, based on the +// provided crypto parameters at the provided MRAM address. The integers are +// written back to RAM at the dest address provided in the crypto parameters. +void ProcessGBACrypto(u32 address); + struct GBAUCode : public UCodeInterface { GBAUCode(DSPHLE *dsphle, u32 crc); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 8700264825..61006e9b44 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -4,6 +4,7 @@ #include "Core/ConfigManager.h" #include "Core/HW/DSPHLE/MailHandler.h" +#include "Core/HW/DSPHLE/UCodes/GBA.h" #include "Core/HW/DSPHLE/UCodes/UCodes.h" #include "Core/HW/DSPHLE/UCodes/Zelda.h" @@ -474,8 +475,7 @@ void ZeldaUCode::RunPendingCommands() case 0x0C: if (m_flags & SUPPORTS_GBA_CRYPTO) { - // TODO - NOTICE_LOG(DSPHLE, "Received an unhandled GBA crypto command, param: %08x", Read32()); + ProcessGBACrypto(Read32()); } else if (m_flags & WEIRD_CMD_0C) { From 090723167f55722d3f650f53988eacb94c6d31cb Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 00:56:48 +0200 Subject: [PATCH 50/59] ZeldaHLE: Implement constant patterns with variable step (sample source 0A). --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 61006e9b44..299e5e4898 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -758,6 +758,7 @@ struct ZeldaAudioRenderer::VPB // Simple pattern stored in the data downloaded by command 01. Playback // frequency is controlled by the resampling ratio. SRC_CONST_PATTERN_0 = 7, + SRC_CONST_PATTERN_0_VARIABLE_STEP = 10, SRC_CONST_PATTERN_1 = 4, SRC_CONST_PATTERN_2 = 11, SRC_CONST_PATTERN_3 = 12, @@ -1320,27 +1321,33 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) } case VPB::SRC_CONST_PATTERN_0: + case VPB::SRC_CONST_PATTERN_0_VARIABLE_STEP: case VPB::SRC_CONST_PATTERN_1: case VPB::SRC_CONST_PATTERN_2: case VPB::SRC_CONST_PATTERN_3: { const u16 PATTERN_SIZE = 0x40; - std::map samples_source_to_pattern = { - { VPB::SRC_CONST_PATTERN_0, 0 }, - { VPB::SRC_CONST_PATTERN_1, 1 }, - { VPB::SRC_CONST_PATTERN_2, 2 }, - { VPB::SRC_CONST_PATTERN_3, 3 }, + struct PatternInfo { u16 idx; bool variable_step; }; + std::map samples_source_to_pattern = { + { VPB::SRC_CONST_PATTERN_0, {0, false} }, + { VPB::SRC_CONST_PATTERN_0_VARIABLE_STEP, {0, true} }, + { VPB::SRC_CONST_PATTERN_1, {1, false} }, + { VPB::SRC_CONST_PATTERN_2, {2, false} }, + { VPB::SRC_CONST_PATTERN_3, {3, false} }, }; - u16 pattern_idx = samples_source_to_pattern[vpb->samples_source_type]; - u16 pattern_offset = pattern_idx * PATTERN_SIZE; + auto& pattern_info = samples_source_to_pattern[vpb->samples_source_type]; + u16 pattern_offset = pattern_info.idx * PATTERN_SIZE; s16* pattern = m_const_patterns.data() + pattern_offset; u32 pos = vpb->current_pos_frac << 6; // log2(PATTERN_SIZE) - u32 step = vpb->resampling_ratio << 5; for (size_t i = 0; i < buffer->size(); ++i) { + u32 step = vpb->resampling_ratio << 5; + if (pattern_info.variable_step) + step += (u32)((u16)m_buf_back_right[i] * vpb->resampling_ratio) >> 10; + (*buffer)[i] = pattern[pos >> 16]; pos = (pos + step) % (PATTERN_SIZE << 16); } From fddb3f0d3c6a668a527f799b3d2f0a30cd2b621b Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 01:06:52 +0200 Subject: [PATCH 51/59] ZeldaHLE: fix the constant pattern variable step computation. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 299e5e4898..ea0e2b97af 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1346,7 +1346,7 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) { u32 step = vpb->resampling_ratio << 5; if (pattern_info.variable_step) - step += (u32)((u16)m_buf_back_right[i] * vpb->resampling_ratio) >> 10; + step += (u32)((u16)m_buf_back_right[i] * vpb->resampling_ratio) >> 26; (*buffer)[i] = pattern[pos >> 16]; pos = (pos + step) % (PATTERN_SIZE << 16); From c6c0f69c6b43d7956640289c19833a15b057333e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 01:27:20 +0200 Subject: [PATCH 52/59] ZeldaHLE: really fix the constant pattern variable step computation. This time tested by comparing pattern indexes across LLE and HLE with the same inputs. Matches on the 3-4 test cases I made. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index ea0e2b97af..69f75bbcb5 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1341,21 +1341,22 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb) s16* pattern = m_const_patterns.data() + pattern_offset; u32 pos = vpb->current_pos_frac << 6; // log2(PATTERN_SIZE) + u32 step = vpb->resampling_ratio << 5; for (size_t i = 0; i < buffer->size(); ++i) { - u32 step = vpb->resampling_ratio << 5; - if (pattern_info.variable_step) - step += (u32)((u16)m_buf_back_right[i] * vpb->resampling_ratio) >> 26; - (*buffer)[i] = pattern[pos >> 16]; pos = (pos + step) % (PATTERN_SIZE << 16); + if (pattern_info.variable_step) + pos = ((pos << 10) + m_buf_back_right[i] * vpb->resampling_ratio) >> 10; } vpb->current_pos_frac = pos >> 6; break; } + + case VPB::SRC_PCM8_FROM_ARAM: DownloadPCMSamplesFromARAM(raw_input_samples.data() + 4, vpb, NeededRawSamplesCount(*vpb)); From dd1cb88e9af3f945e3c87a1527dd332a67d0a2a0 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 01:46:33 +0200 Subject: [PATCH 53/59] ZeldaHLE: Add new UCode version for Pikmin 1/2 New Play Control --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index 517a6fe190..b840e1da10 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -62,6 +62,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x42f64ac4: // Luigi's Mansion - US case 0x56d36052: // Super Mario Sunshine - US case 0x6c3f6f94: // Zelda TP Wii - US + case 0xeaeb38cc: // Pikmin 1/2 New Play Control Wii - US return new ZeldaUCode(dsphle, crc); case 0x2ea36ce6: // Some Wii demos diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 69f75bbcb5..9016b76f18 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -84,12 +84,13 @@ static const std::map UCODE_FLAGS = { // Super Mario Galaxy. // Super Mario Galaxy 2. { 0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER }, + // Pikmin 1/2 New Play Control. + { 0xEAEB38CC, NO_ARAM | MAKE_DOLBY_LOUDER }, // TODO: Other games that use this UCode (exhaustive list): // * Link's Crossbow Training // * The Legend of Zelda: Collector's Edition // * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????) - // * Pikmin 1/2 New Play Control! }; ZeldaUCode::ZeldaUCode(DSPHLE *dsphle, u32 crc) From a602466e4f5a7f6fdd36cd1dfef8c68eeaeb7b7c Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 03:39:26 +0200 Subject: [PATCH 54/59] ZeldaHLE: Implement patterns 2/3 automatic regeneration. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 9016b76f18..dcefb6bfdf 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -929,7 +929,24 @@ void ZeldaAudioRenderer::PrepareFrame() m_buf_back_left_reverb.fill(0); m_buf_back_right_reverb.fill(0); - // TODO: Prepare patterns 2/3 - they are not constant unlike 0/1. + // Prepare patterns 2/3 - they are not constant unlike 0/1. + s16* pattern2 = m_const_patterns.data() + 2 * 0x40; + s32 yn2 = pattern2[0x40 - 2], yn1 = pattern2[0x40 - 1], v; + for (int i = 0; i < 0x40; i += 2) + { + v = yn2 * yn1 - (pattern2[i] << 16); + yn2 = yn1; yn1 = pattern2[i]; pattern2[i] = v >> 16; + + v = 2 * (yn2 * yn1 + (pattern2[i + 1] << 16)); + yn2 = yn1; yn1 = pattern2[i + 1]; pattern2[i + 1] = v >> 16; + } + s16* pattern3 = m_const_patterns.data() + 3 * 0x40; + yn2 = pattern3[0x40 - 2]; yn1 = pattern3[0x40 - 1]; + s16 acc = yn1; + s16 step = pattern3[0] + ((yn1 * yn2 + ((yn2 << 16) + yn1)) >> 16); + step = (step & 0x1FF) | 0x2000; + for (s32 i = 0; i < 0x40; ++i) + pattern3[i] = acc + (i + 1) * step; m_prepared = true; } From fcbed7483d709ec0c36f6a2cb7559344a38afd3e Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 19:02:11 +0200 Subject: [PATCH 55/59] ZeldaHLE: Properly read remaining AFC samples when a whole frame is available. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index dcefb6bfdf..122713d592 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1544,8 +1544,9 @@ void ZeldaAudioRenderer::DownloadAFCSamplesFromARAM( // Try to push currently cached/already decoded samples. u16 remaining_to_output = std::min(vpb->afc_remaining_decoded_samples, requested_samples_count); - for (size_t i = 0x10 - remaining_to_output; i < 0x10; ++i) - *dst++ = vpb->afc_remaining_samples[i]; + s16* base = &vpb->afc_remaining_samples[0x10 - vpb->afc_remaining_decoded_samples]; + for (size_t i = 0; i < remaining_to_output; ++i) + *dst++ = base[i]; vpb->afc_remaining_decoded_samples -= remaining_to_output; requested_samples_count -= remaining_to_output; From 9105a76eb196623c74858f216ffef179052a930b Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sun, 9 Aug 2015 21:38:57 +0200 Subject: [PATCH 56/59] ZeldaHLE: Comment on the differences between FSA/ZTP UCodes. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 122713d592..ace118b2f9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -78,6 +78,9 @@ static const std::map UCODE_FLAGS = { { 0x2FCDF1EC, MAKE_DOLBY_LOUDER }, // The Legend of Zelda: Twilight Princess / GC. // Donkey Kong Jungle Beat. + // + // TODO: These do additional filtering at frame rendering time. We don't + // implement this yet. { 0x6CA33A6D, MAKE_DOLBY_LOUDER }, // The Legend of Zelda: Twilight Princess / Wii. { 0x6C3F6F94, NO_ARAM | MAKE_DOLBY_LOUDER }, From 18d0f1588589417a63e399caaf3e83ecc971304b Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 14 Aug 2015 11:32:24 +0200 Subject: [PATCH 57/59] ZeldaHLE: Fix the AFC remaining samples logic on looping. --- Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index ace118b2f9..6e5e93c25d 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -1579,8 +1579,19 @@ void ZeldaAudioRenderer::DownloadAFCSamplesFromARAM( DecodeAFC(vpb, dst, requested_blocks_count); - for (size_t i = 0; i < 0x10; ++i) - vpb->afc_remaining_samples[i] = dst[decoded_samples_count - 0x10 + i]; + if (vpb->afc_remaining_decoded_samples) + { + for (size_t i = 0; i < 0x10; ++i) + vpb->afc_remaining_samples[i] = dst[decoded_samples_count - 0x10 + i]; + + if (!vpb->GetRemainingLength() && vpb->GetLoopStartPosition()) + { + // Adjust remaining samples to account for the future loop iteration. + base = vpb->afc_remaining_samples + ((vpb->GetLoopStartPosition() + 0xF) & 0xF); + for (size_t i = 0; i < vpb->afc_remaining_decoded_samples; ++i) + vpb->afc_remaining_samples[0x10 - i - 1] = *base--; + } + } return; } From 2d5d203be81b6e14b5e21e4a6f3987bd4e016e06 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 14 Aug 2015 13:03:28 +0200 Subject: [PATCH 58/59] ZeldaHLE: Add UCode version for Pikmin 1 PAL. --- Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp | 1 + Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp index b840e1da10..2ecb0ec270 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/UCodes.cpp @@ -59,6 +59,7 @@ UCodeInterface* UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii) case 0x24b22038: // GC IPL - US case 0x2fcdf1ec: // Zelda FSA - US case 0x4be6a5cb: // Pikmin 1 GC - US + case 0x267fd05a: // Pikmin 1 GC - PAL case 0x42f64ac4: // Luigi's Mansion - US case 0x56d36052: // Super Mario Sunshine - US case 0x6c3f6f94: // Zelda TP Wii - US diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp index 6e5e93c25d..efebb44f8b 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp +++ b/Source/Core/Core/HW/DSPHLE/UCodes/Zelda.cpp @@ -68,6 +68,8 @@ static const std::map UCODE_FLAGS = { { 0x4BE6A5CB, LIGHT_PROTOCOL | NO_CMD_0D | SUPPORTS_GBA_CRYPTO }, // Luigi's Mansion. { 0x42F64AC4, LIGHT_PROTOCOL | NO_CMD_0D | WEIRD_CMD_0C }, + // Pikmin 1 GC PAL. + { 0x267FD05A, SYNC_PER_FRAME | NO_CMD_0D }, // Super Mario Sunshine. { 0x56D36052, SYNC_PER_FRAME | NO_CMD_0D }, // The Legend of Zelda: The Wind Waker. From 86c28bfac6b49011dcd5ee237746271073c17ad2 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Fri, 14 Aug 2015 16:17:28 +0200 Subject: [PATCH 59/59] Bump save state version. --- Source/Core/Core/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 1ace08f922..d60c6aabac 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -66,7 +66,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 44; // Last changed in PR 2464 +static const u32 STATE_VERSION = 45; // Last changed in PR 2846 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list,