SPU: Fix a few cases where SPU interrupts weren't firing

This commit is contained in:
Connor McLaughlin 2021-01-10 01:38:39 +10:00
parent df98a0b04e
commit 15652b4c1f
5 changed files with 100 additions and 57 deletions

View File

@ -158,6 +158,12 @@ void DMA::WriteRegister(u32 offset, u32 value)
case 0x08: case 0x08:
{ {
// HACK: Due to running DMA in slices, we can't wait for the current halt time to finish before running the
// first block of a new channel. This affects games like FF8, where they kick a SPU transfer while a GPU
// transfer is happening, and the SPU transfer gets delayed until the GPU transfer unhalts and finishes, and
// breaks the interrupt.
const bool ignore_halt = !state.channel_control.enable_busy && (value & (1u << 24));
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) | state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
(value & ChannelState::ChannelControl::WRITE_MASK); (value & ChannelState::ChannelControl::WRITE_MASK);
Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits); Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
@ -166,7 +172,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
if (static_cast<Channel>(channel_index) == Channel::OTC) if (static_cast<Channel>(channel_index) == Channel::OTC)
SetRequest(static_cast<Channel>(channel_index), state.channel_control.start_trigger); SetRequest(static_cast<Channel>(channel_index), state.channel_control.start_trigger);
if (CanTransferChannel(static_cast<Channel>(channel_index))) if (CanTransferChannel(static_cast<Channel>(channel_index), ignore_halt))
TransferChannel(static_cast<Channel>(channel_index)); TransferChannel(static_cast<Channel>(channel_index));
return; return;
} }
@ -186,7 +192,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
for (u32 i = 0; i < NUM_CHANNELS; i++) for (u32 i = 0; i < NUM_CHANNELS; i++)
{ {
if (CanTransferChannel(static_cast<Channel>(i))) if (CanTransferChannel(static_cast<Channel>(i), false))
{ {
if (!TransferChannel(static_cast<Channel>(i))) if (!TransferChannel(static_cast<Channel>(i)))
break; break;
@ -220,11 +226,11 @@ void DMA::SetRequest(Channel channel, bool request)
return; return;
cs.request = request; cs.request = request;
if (CanTransferChannel(channel)) if (CanTransferChannel(channel, false))
TransferChannel(channel); TransferChannel(channel);
} }
bool DMA::CanTransferChannel(Channel channel) const bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) const
{ {
if (!m_DPCR.GetMasterEnable(channel)) if (!m_DPCR.GetMasterEnable(channel))
return false; return false;
@ -233,7 +239,7 @@ bool DMA::CanTransferChannel(Channel channel) const
if (!cs.channel_control.enable_busy) if (!cs.channel_control.enable_busy)
return false; return false;
if (cs.channel_control.sync_mode != SyncMode::Manual && IsTransferHalted()) if (cs.channel_control.sync_mode != SyncMode::Manual && (IsTransferHalted() && !ignore_halt))
return false; return false;
return cs.request; return cs.request;
@ -451,6 +457,8 @@ void DMA::HaltTransfer(TickCount duration)
{ {
m_halt_ticks_remaining += duration; m_halt_ticks_remaining += duration;
Log_DebugPrintf("Halting DMA for %d ticks", m_halt_ticks_remaining); Log_DebugPrintf("Halting DMA for %d ticks", m_halt_ticks_remaining);
if (m_unhalt_event->IsActive())
return;
DebugAssert(!m_unhalt_event->IsActive()); DebugAssert(!m_unhalt_event->IsActive());
m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining); m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining);
@ -466,7 +474,7 @@ void DMA::UnhaltTransfer(TickCount ticks)
// Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL. // Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL.
for (u32 i = 0; i < NUM_CHANNELS; i++) for (u32 i = 0; i < NUM_CHANNELS; i++)
{ {
if (CanTransferChannel(static_cast<Channel>(i))) if (CanTransferChannel(static_cast<Channel>(i), false))
{ {
if (!TransferChannel(static_cast<Channel>(i))) if (!TransferChannel(static_cast<Channel>(i)))
return; return;

View File

@ -62,7 +62,7 @@ private:
void ClearState(); void ClearState();
// is everything enabled for a channel to operate? // is everything enabled for a channel to operate?
bool CanTransferChannel(Channel channel) const; bool CanTransferChannel(Channel channel, bool ignore_halt) const;
bool IsTransferHalted() const; bool IsTransferHalted() const;
void UpdateIRQ(); void UpdateIRQ();

View File

@ -254,8 +254,7 @@ u16 SPU::ReadRegister(u32 offset)
return m_transfer_control.bits; return m_transfer_control.bits;
case 0x1F801DAE - SPU_BASE: case 0x1F801DAE - SPU_BASE:
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_transfer_event->InvokeEarly();
Log_TracePrintf("SPU status register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits)); Log_TracePrintf("SPU status register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits));
return m_SPUSTAT.bits; return m_SPUSTAT.bits;
@ -272,11 +271,11 @@ u16 SPU::ReadRegister(u32 offset)
return m_external_volume_right; return m_external_volume_right;
case 0x1F801DB8 - SPU_BASE: case 0x1F801DB8 - SPU_BASE:
m_tick_event->InvokeEarly(); GeneratePendingSamples();
return m_main_volume_left.current_level; return m_main_volume_left.current_level;
case 0x1F801DBA - SPU_BASE: case 0x1F801DBA - SPU_BASE:
m_tick_event->InvokeEarly(); GeneratePendingSamples();
return m_main_volume_right.current_level; return m_main_volume_right.current_level;
default: default:
@ -290,7 +289,7 @@ u16 SPU::ReadRegister(u32 offset)
if (offset >= (0x1F801E00 - SPU_BASE) && offset < (0x1F801E60 - SPU_BASE)) if (offset >= (0x1F801E00 - SPU_BASE) && offset < (0x1F801E60 - SPU_BASE))
{ {
const u32 voice_index = (offset - (0x1F801E00 - SPU_BASE)) / 4; const u32 voice_index = (offset - (0x1F801E00 - SPU_BASE)) / 4;
m_tick_event->InvokeEarly(); GeneratePendingSamples();
if (offset & 0x02) if (offset & 0x02)
return m_voices[voice_index].left_volume.current_level; return m_voices[voice_index].left_volume.current_level;
else else
@ -310,7 +309,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D80 - SPU_BASE: case 0x1F801D80 - SPU_BASE:
{ {
Log_DebugPrintf("SPU main volume left <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU main volume left <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_main_volume_left_reg.bits = value; m_main_volume_left_reg.bits = value;
m_main_volume_left.Reset(m_main_volume_left_reg); m_main_volume_left.Reset(m_main_volume_left_reg);
return; return;
@ -319,7 +318,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D82 - SPU_BASE: case 0x1F801D82 - SPU_BASE:
{ {
Log_DebugPrintf("SPU main volume right <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU main volume right <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_main_volume_right_reg.bits = value; m_main_volume_right_reg.bits = value;
m_main_volume_right.Reset(m_main_volume_right_reg); m_main_volume_right.Reset(m_main_volume_right_reg);
return; return;
@ -328,7 +327,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D84 - SPU_BASE: case 0x1F801D84 - SPU_BASE:
{ {
Log_DebugPrintf("SPU reverb output volume left <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb output volume left <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_registers.vLOUT = value; m_reverb_registers.vLOUT = value;
return; return;
} }
@ -336,7 +335,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D86 - SPU_BASE: case 0x1F801D86 - SPU_BASE:
{ {
Log_DebugPrintf("SPU reverb output volume right <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb output volume right <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_registers.vROUT = value; m_reverb_registers.vROUT = value;
return; return;
} }
@ -344,7 +343,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D88 - SPU_BASE: case 0x1F801D88 - SPU_BASE:
{ {
Log_DebugPrintf("SPU key on low <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU key on low <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_key_on_register = (m_key_on_register & 0xFFFF0000) | ZeroExtend32(value); m_key_on_register = (m_key_on_register & 0xFFFF0000) | ZeroExtend32(value);
} }
break; break;
@ -352,7 +351,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D8A - SPU_BASE: case 0x1F801D8A - SPU_BASE:
{ {
Log_DebugPrintf("SPU key on high <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU key on high <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_key_on_register = (m_key_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); m_key_on_register = (m_key_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
} }
break; break;
@ -360,7 +359,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D8C - SPU_BASE: case 0x1F801D8C - SPU_BASE:
{ {
Log_DebugPrintf("SPU key off low <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU key off low <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_key_off_register = (m_key_off_register & 0xFFFF0000) | ZeroExtend32(value); m_key_off_register = (m_key_off_register & 0xFFFF0000) | ZeroExtend32(value);
} }
break; break;
@ -368,14 +367,14 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D8E - SPU_BASE: case 0x1F801D8E - SPU_BASE:
{ {
Log_DebugPrintf("SPU key off high <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU key off high <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_key_off_register = (m_key_off_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); m_key_off_register = (m_key_off_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
} }
break; break;
case 0x1F801D90 - SPU_BASE: case 0x1F801D90 - SPU_BASE:
{ {
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_pitch_modulation_enable_register = (m_pitch_modulation_enable_register & 0xFFFF0000) | ZeroExtend32(value); m_pitch_modulation_enable_register = (m_pitch_modulation_enable_register & 0xFFFF0000) | ZeroExtend32(value);
Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register); Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register);
} }
@ -383,7 +382,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D92 - SPU_BASE: case 0x1F801D92 - SPU_BASE:
{ {
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_pitch_modulation_enable_register = m_pitch_modulation_enable_register =
(m_pitch_modulation_enable_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); (m_pitch_modulation_enable_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register); Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register);
@ -393,7 +392,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D94 - SPU_BASE: case 0x1F801D94 - SPU_BASE:
{ {
Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_noise_mode_register = (m_noise_mode_register & 0xFFFF0000) | ZeroExtend32(value); m_noise_mode_register = (m_noise_mode_register & 0xFFFF0000) | ZeroExtend32(value);
} }
break; break;
@ -401,7 +400,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D96 - SPU_BASE: case 0x1F801D96 - SPU_BASE:
{ {
Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_noise_mode_register = (m_noise_mode_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); m_noise_mode_register = (m_noise_mode_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
} }
break; break;
@ -409,7 +408,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D98 - SPU_BASE: case 0x1F801D98 - SPU_BASE:
{ {
Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_on_register = (m_reverb_on_register & 0xFFFF0000) | ZeroExtend32(value); m_reverb_on_register = (m_reverb_on_register & 0xFFFF0000) | ZeroExtend32(value);
} }
break; break;
@ -417,7 +416,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801D9A - SPU_BASE: case 0x1F801D9A - SPU_BASE:
{ {
Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_on_register = (m_reverb_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16); m_reverb_on_register = (m_reverb_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
} }
break; break;
@ -425,7 +424,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DA2 - SPU_BASE: case 0x1F801DA2 - SPU_BASE:
{ {
Log_DebugPrintf("SPU reverb base address < 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU reverb base address < 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_registers.mBASE = value; m_reverb_registers.mBASE = value;
m_reverb_base_address = ZeroExtend32(value << 2) & 0x3FFFFu; m_reverb_base_address = ZeroExtend32(value << 2) & 0x3FFFFu;
m_reverb_current_address = m_reverb_base_address; m_reverb_current_address = m_reverb_base_address;
@ -435,10 +434,10 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DA4 - SPU_BASE: case 0x1F801DA4 - SPU_BASE:
{ {
Log_DebugPrintf("SPU IRQ address register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU IRQ address register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_irq_address = value; m_irq_address = value;
if (m_SPUCNT.irq9_enable) if (IsRAMIRQTriggerable())
CheckForLateRAMIRQs(); CheckForLateRAMIRQs();
return; return;
@ -447,9 +446,14 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DA6 - SPU_BASE: case 0x1F801DA6 - SPU_BASE:
{ {
Log_DebugPrintf("SPU transfer address register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU transfer address register <- 0x%04X", ZeroExtend32(value));
m_transfer_event->InvokeEarly();
m_transfer_address_reg = value; m_transfer_address_reg = value;
m_transfer_address = ZeroExtend32(value) * 8; m_transfer_address = ZeroExtend32(value) * 8;
CheckRAMIRQ(m_transfer_address); if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer address reg set", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
}
return; return;
} }
@ -465,7 +469,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DAA - SPU_BASE: case 0x1F801DAA - SPU_BASE:
{ {
Log_DebugPrintf("SPU control register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU control register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(true); GeneratePendingSamples();
const SPUCNT new_value{value}; const SPUCNT new_value{value};
if (new_value.ram_transfer_mode != m_SPUCNT.ram_transfer_mode && if (new_value.ram_transfer_mode != m_SPUCNT.ram_transfer_mode &&
@ -494,7 +498,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
if (!m_SPUCNT.irq9_enable) if (!m_SPUCNT.irq9_enable)
m_SPUSTAT.irq9_flag = false; m_SPUSTAT.irq9_flag = false;
else if (!m_SPUSTAT.irq9_flag) else if (IsRAMIRQTriggerable())
CheckForLateRAMIRQs(); CheckForLateRAMIRQs();
UpdateEventInterval(); UpdateEventInterval();
@ -513,7 +517,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DB0 - SPU_BASE: case 0x1F801DB0 - SPU_BASE:
{ {
Log_DebugPrintf("SPU left cd audio register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU left cd audio register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_cd_audio_volume_left = value; m_cd_audio_volume_left = value;
} }
break; break;
@ -521,7 +525,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
case 0x1F801DB2 - SPU_BASE: case 0x1F801DB2 - SPU_BASE:
{ {
Log_DebugPrintf("SPU right cd audio register <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU right cd audio register <- 0x%04X", ZeroExtend32(value));
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_cd_audio_volume_right = value; m_cd_audio_volume_right = value;
} }
break; break;
@ -560,7 +564,7 @@ void SPU::WriteRegister(u32 offset, u16 value)
{ {
const u32 reg = (offset - (0x1F801DC0 - SPU_BASE)) / 2; const u32 reg = (offset - (0x1F801DC0 - SPU_BASE)) / 2;
Log_DebugPrintf("SPU reverb register %u <- 0x%04X", reg, value); Log_DebugPrintf("SPU reverb register %u <- 0x%04X", reg, value);
m_tick_event->InvokeEarly(); GeneratePendingSamples();
m_reverb_registers.rev[reg] = value; m_reverb_registers.rev[reg] = value;
return; return;
} }
@ -581,7 +585,7 @@ u16 SPU::ReadVoiceRegister(u32 offset)
// ADSR volume needs to be updated when reading. A voice might be off as well, but key on is pending. // ADSR volume needs to be updated when reading. A voice might be off as well, but key on is pending.
const Voice& voice = m_voices[voice_index]; const Voice& voice = m_voices[voice_index];
if (reg_index >= 6 && (voice.IsOn() || m_key_on_register & (1u << voice_index))) if (reg_index >= 6 && (voice.IsOn() || m_key_on_register & (1u << voice_index)))
m_tick_event->InvokeEarly(); GeneratePendingSamples();
Log_TracePrintf("Read voice %u register %u -> 0x%02X", voice_index, reg_index, voice.regs.index[reg_index]); Log_TracePrintf("Read voice %u register %u -> 0x%02X", voice_index, reg_index, voice.regs.index[reg_index]);
return voice.regs.index[reg_index]; return voice.regs.index[reg_index];
@ -596,7 +600,7 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
Voice& voice = m_voices[voice_index]; Voice& voice = m_voices[voice_index];
if (voice.IsOn() || m_key_on_register & (1u << voice_index)) if (voice.IsOn() || m_key_on_register & (1u << voice_index))
m_tick_event->InvokeEarly(); GeneratePendingSamples();
switch (reg_index) switch (reg_index)
{ {
@ -685,22 +689,23 @@ void SPU::WriteVoiceRegister(u32 offset, u16 value)
} }
} }
void SPU::CheckRAMIRQ(u32 address) void SPU::TriggerRAMIRQ()
{ {
if (!m_SPUCNT.irq9_enable) DebugAssert(IsRAMIRQTriggerable());
return;
if (ZeroExtend32(m_irq_address) * 8 == address)
{
Log_DebugPrintf("SPU IRQ at address 0x%08X", address);
m_SPUSTAT.irq9_flag = true; m_SPUSTAT.irq9_flag = true;
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SPU); g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SPU);
} }
}
void SPU::CheckForLateRAMIRQs() void SPU::CheckForLateRAMIRQs()
{ {
for (u32 i = 0; i < NUM_VOICES && !m_SPUSTAT.irq9_flag; i++) if (CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from late transfer", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
return;
}
for (u32 i = 0; i < NUM_VOICES; i++)
{ {
// we skip voices which haven't started this block yet - because they'll check // we skip voices which haven't started this block yet - because they'll check
// the next time they're sampled, and the delay might be important. // the next time they're sampled, and the delay might be important.
@ -708,8 +713,13 @@ void SPU::CheckForLateRAMIRQs()
if (!v.has_samples) if (!v.has_samples)
continue; continue;
CheckRAMIRQ(v.current_address * 8); const u32 address = v.current_address * 8;
CheckRAMIRQ(v.current_address * 8 + 8); if (CheckRAMIRQ(address) || CheckRAMIRQ((address + 8) & RAM_MASK))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from late", address, address / 8);
TriggerRAMIRQ();
return;
}
} }
} }
@ -718,7 +728,11 @@ void SPU::WriteToCaptureBuffer(u32 index, s16 value)
const u32 ram_address = (index * CAPTURE_BUFFER_SIZE_PER_CHANNEL) | ZeroExtend16(m_capture_buffer_position); const u32 ram_address = (index * CAPTURE_BUFFER_SIZE_PER_CHANNEL) | ZeroExtend16(m_capture_buffer_position);
// Log_DebugPrintf("write to capture buffer %u (0x%08X) <- 0x%04X", index, ram_address, u16(value)); // Log_DebugPrintf("write to capture buffer %u (0x%08X) <- 0x%04X", index, ram_address, u16(value));
std::memcpy(&m_ram[ram_address], &value, sizeof(value)); std::memcpy(&m_ram[ram_address], &value, sizeof(value));
CheckRAMIRQ(ram_address); if (IsRAMIRQTriggerable() && CheckRAMIRQ(ram_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from capture buffer", ram_address, ram_address / 8);
TriggerRAMIRQ();
}
} }
void SPU::IncrementCaptureBufferPosition() void SPU::IncrementCaptureBufferPosition()
@ -744,6 +758,12 @@ void SPU::ExecuteTransfer(TickCount ticks)
m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK; m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK;
m_transfer_fifo.Push(value); m_transfer_fifo.Push(value);
ticks -= TRANSFER_TICKS_PER_HALFWORD; ticks -= TRANSFER_TICKS_PER_HALFWORD;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer read", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
}
} }
// this can result in the FIFO being emptied, hence double the while loop // this can result in the FIFO being emptied, hence double the while loop
@ -774,6 +794,12 @@ void SPU::ExecuteTransfer(TickCount ticks)
std::memcpy(&m_ram[m_transfer_address], &value, sizeof(u16)); std::memcpy(&m_ram[m_transfer_address], &value, sizeof(u16));
m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK; m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK;
ticks -= TRANSFER_TICKS_PER_HALFWORD; ticks -= TRANSFER_TICKS_PER_HALFWORD;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer write", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
}
} }
// similar deal here, the FIFO can be written out in a long slice // similar deal here, the FIFO can be written out in a long slice
@ -951,6 +977,9 @@ void SPU::DMAWrite(const u32* words, u32 word_count)
void SPU::GeneratePendingSamples() void SPU::GeneratePendingSamples()
{ {
if (m_transfer_event->IsActive())
m_transfer_event->InvokeEarly();
m_tick_event->InvokeEarly(); m_tick_event->InvokeEarly();
} }
@ -1322,8 +1351,11 @@ s32 SPU::Voice::Interpolate() const
void SPU::ReadADPCMBlock(u16 address, ADPCMBlock* block) void SPU::ReadADPCMBlock(u16 address, ADPCMBlock* block)
{ {
u32 ram_address = (ZeroExtend32(address) * 8) & RAM_MASK; u32 ram_address = (ZeroExtend32(address) * 8) & RAM_MASK;
CheckRAMIRQ(ram_address); if (IsRAMIRQTriggerable() && (CheckRAMIRQ(ram_address) || CheckRAMIRQ((ram_address + 8) & RAM_MASK)))
CheckRAMIRQ((ram_address + 8) & RAM_MASK); {
Log_DebugPrintf("Trigger IRQ @ %08X %04X from ADPCM reader", ram_address, ram_address / 8);
TriggerRAMIRQ();
}
// fast path - no wrap-around // fast path - no wrap-around
if ((ram_address + sizeof(ADPCMBlock)) <= RAM_SIZE) if ((ram_address + sizeof(ADPCMBlock)) <= RAM_SIZE)

View File

@ -334,8 +334,11 @@ private:
u16 ReadVoiceRegister(u32 offset); u16 ReadVoiceRegister(u32 offset);
void WriteVoiceRegister(u32 offset, u16 value); void WriteVoiceRegister(u32 offset, u16 value);
void CheckRAMIRQ(u32 address); ALWAYS_INLINE bool IsRAMIRQTriggerable() const { return m_SPUCNT.irq9_enable && !m_SPUSTAT.irq9_flag; }
ALWAYS_INLINE bool CheckRAMIRQ(u32 address) const { return ((ZeroExtend32(m_irq_address) * 8) == address); }
void TriggerRAMIRQ();
void CheckForLateRAMIRQs(); void CheckForLateRAMIRQs();
void WriteToCaptureBuffer(u32 index, s16 value); void WriteToCaptureBuffer(u32 index, s16 value);
void IncrementCaptureBufferPosition(); void IncrementCaptureBufferPosition();

View File

@ -17,8 +17,8 @@ public:
TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback); TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
~TimingEvent(); ~TimingEvent();
const std::string& GetName() const { return m_name; } ALWAYS_INLINE const std::string& GetName() const { return m_name; }
bool IsActive() const { return m_active; } ALWAYS_INLINE bool IsActive() const { return m_active; }
// Returns the number of ticks between each event. // Returns the number of ticks between each event.
ALWAYS_INLINE TickCount GetPeriod() const { return m_period; } ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }