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.
This commit is contained in:
Pierre Bourdon 2014-12-27 12:15:27 +01:00
parent 89037781e0
commit addb5cb887
3 changed files with 30 additions and 6 deletions

View File

@ -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

View File

@ -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<u32, u32> UCODE_FLAGS = {
@ -53,6 +56,8 @@ static const std::map<u32, u32> 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;

View File

@ -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<u32, 256> m_sync_voice_skip_flags{};
std::array<u16, 256> 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