CDROM: Implement auto-pause at end of track

This commit is contained in:
Connor McLaughlin 2019-11-12 20:31:59 +10:00
parent 85413218cb
commit 8e06f86db8
3 changed files with 59 additions and 25 deletions

View file

@ -142,6 +142,12 @@ public:
u8 data[SUBCHANNEL_BYTES_PER_FRAME]; u8 data[SUBCHANNEL_BYTES_PER_FRAME];
u16 ComputeCRC() const; u16 ComputeCRC() const;
SubChannelQ& operator=(const SubChannelQ& q)
{
std::copy(q.data, q.data + SUBCHANNEL_BYTES_PER_FRAME, data);
return *this;
}
}; };
static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size"); static_assert(sizeof(SubChannelQ) == SUBCHANNEL_BYTES_PER_FRAME, "SubChannelQ is correct size");

View file

@ -106,6 +106,7 @@ bool CDROM::DoState(StateWrapper& sw)
sw.DoBytes(&m_last_sector_subheader, sizeof(m_last_sector_subheader)); sw.DoBytes(&m_last_sector_subheader, sizeof(m_last_sector_subheader));
sw.DoBytes(&m_last_subq, sizeof(m_last_subq)); sw.DoBytes(&m_last_subq, sizeof(m_last_subq));
sw.Do(&m_last_cdda_report_frame_nibble); sw.Do(&m_last_cdda_report_frame_nibble);
sw.Do(&m_auto_pause_track_number);
sw.Do(&m_cd_audio_volume_matrix); sw.Do(&m_cd_audio_volume_matrix);
sw.Do(&m_next_cd_audio_volume_matrix); sw.Do(&m_next_cd_audio_volume_matrix);
sw.Do(&m_xa_last_samples); sw.Do(&m_xa_last_samples);
@ -432,7 +433,7 @@ void CDROM::SetAsyncInterrupt(Interrupt interrupt)
DeliverAsyncInterrupt(); DeliverAsyncInterrupt();
} }
void CDROM::CancelAsyncInterrupt() void CDROM::ClearAsyncInterrupt()
{ {
m_pending_async_interrupt = 0; m_pending_async_interrupt = 0;
m_async_response_fifo.Clear(); m_async_response_fifo.Clear();
@ -1046,6 +1047,7 @@ void CDROM::BeginReading(bool cdda)
m_sector_buffer.clear(); m_sector_buffer.clear();
m_last_cdda_report_frame_nibble = 0xFF; m_last_cdda_report_frame_nibble = 0xFF;
m_auto_pause_track_number = Truncate8(m_media->GetTrackNumber());
m_drive_state = cdda ? DriveState::Playing : DriveState::Reading; m_drive_state = cdda ? DriveState::Playing : DriveState::Reading;
m_drive_remaining_ticks = GetTicksForRead(); m_drive_remaining_ticks = GetTicksForRead();
@ -1175,14 +1177,33 @@ void CDROM::DoTOCRead()
void CDROM::DoSectorRead() void CDROM::DoSectorRead()
{ {
// TODO: Error handling // TODO: Error handling
CDImage::SubChannelQ subq;
if (!m_media->ReadSubChannelQ(&subq))
Panic("SubChannel Q read failed");
if (m_mode.auto_pause && BCDToDecimal(subq.track_number_bcd) != m_auto_pause_track_number)
{
// we don't want to update the position if the track changes, so we check it before reading the actual sector.
Log_DevPrintf("Auto pause at the end of track %u (LBA %u)", m_auto_pause_track_number,
m_media->GetPositionOnDisc());
ClearAsyncInterrupt();
m_async_response_fifo.Push(m_secondary_status.bits);
SetAsyncInterrupt(Interrupt::INT4);
m_secondary_status.ClearActiveBits();
m_drive_state = DriveState::Idle;
return;
}
u8 raw_sector[CDImage::RAW_SECTOR_SIZE]; u8 raw_sector[CDImage::RAW_SECTOR_SIZE];
if (!m_media->ReadSubChannelQ(&m_last_subq) || !m_media->ReadRawSector(raw_sector)) if (!m_media->ReadRawSector(raw_sector))
Panic("Sector read failed"); Panic("Sector read failed");
if (m_drive_state == DriveState::Reading) if (m_drive_state == DriveState::Reading)
ProcessDataSector(raw_sector); ProcessDataSector(raw_sector, subq);
else if (m_drive_state == DriveState::Playing) else if (m_drive_state == DriveState::Playing)
ProcessCDDASector(raw_sector); ProcessCDDASector(raw_sector, subq);
else else
Panic("Not reading or playing"); Panic("Not reading or playing");
@ -1190,11 +1211,15 @@ void CDROM::DoSectorRead()
m_system->SetDowncount(m_drive_remaining_ticks); m_system->SetDowncount(m_drive_remaining_ticks);
} }
void CDROM::ProcessDataSector(const u8* raw_sector) void CDROM::ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
{ {
std::memcpy(&m_last_sector_header, &raw_sector[SECTOR_SYNC_SIZE], sizeof(m_last_sector_header)); std::memcpy(&m_last_sector_header, &raw_sector[SECTOR_SYNC_SIZE], sizeof(m_last_sector_header));
std::memcpy(&m_last_sector_subheader, &raw_sector[SECTOR_SYNC_SIZE + sizeof(m_last_sector_header)], std::memcpy(&m_last_sector_subheader, &raw_sector[SECTOR_SYNC_SIZE + sizeof(m_last_sector_header)],
sizeof(m_last_sector_subheader)); sizeof(m_last_sector_subheader));
// TODO: Check SubQ checksum.
m_last_subq = subq;
m_secondary_status.header_valid = true; m_secondary_status.header_valid = true;
Log_DevPrintf("Read sector %u: mode %u submode 0x%02X", m_media->GetPositionOnDisc() - 1, Log_DevPrintf("Read sector %u: mode %u submode 0x%02X", m_media->GetPositionOnDisc() - 1,
@ -1219,7 +1244,7 @@ void CDROM::ProcessDataSector(const u8* raw_sector)
} }
else else
{ {
ProcessXAADPCMSector(raw_sector); ProcessXAADPCMSector(raw_sector, subq);
} }
// Audio+realtime sectors aren't delivered to the CPU. // Audio+realtime sectors aren't delivered to the CPU.
@ -1231,7 +1256,7 @@ void CDROM::ProcessDataSector(const u8* raw_sector)
if (HasPendingAsyncInterrupt()) if (HasPendingAsyncInterrupt())
{ {
Log_WarningPrintf("Data interrupt was not delivered"); Log_WarningPrintf("Data interrupt was not delivered");
CancelAsyncInterrupt(); ClearAsyncInterrupt();
} }
if (!m_sector_buffer.empty()) if (!m_sector_buffer.empty())
{ {
@ -1337,7 +1362,7 @@ static void ResampleXAADPCM(const s16* samples_in, u32 num_samples_in, SPU* spu,
*sixstep_ptr = sixstep; *sixstep_ptr = sixstep;
} }
void CDROM::ProcessXAADPCMSector(const u8* raw_sector) void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
{ {
std::array<s16, CDXA::XA_ADPCM_SAMPLES_PER_SECTOR_4BIT> sample_buffer; std::array<s16, CDXA::XA_ADPCM_SAMPLES_PER_SECTOR_4BIT> sample_buffer;
CDXA::DecodeADPCMSector(raw_sector, sample_buffer.data(), m_xa_last_samples.data()); CDXA::DecodeADPCMSector(raw_sector, sample_buffer.data(), m_xa_last_samples.data());
@ -1384,7 +1409,7 @@ void CDROM::ProcessXAADPCMSector(const u8* raw_sector)
} }
} }
void CDROM::ProcessCDDASector(const u8* raw_sector) void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
{ {
// For CDDA sectors, the whole sector contains the audio data. // For CDDA sectors, the whole sector contains the audio data.
Log_DevPrintf("Read sector %u as CDDA", m_media->GetPositionOnDisc()); Log_DevPrintf("Read sector %u as CDDA", m_media->GetPositionOnDisc());
@ -1396,25 +1421,25 @@ void CDROM::ProcessCDDASector(const u8* raw_sector)
{ {
m_last_cdda_report_frame_nibble = frame_nibble; m_last_cdda_report_frame_nibble = frame_nibble;
Log_DebugPrintf("CDDA report at track[%02x] index[%02x] rel[%02x:%02x:%02x]", m_last_subq.track_number_bcd, Log_DebugPrintf("CDDA report at track[%02x] index[%02x] rel[%02x:%02x:%02x]", subq.track_number_bcd,
m_last_subq.index_number_bcd, m_last_subq.relative_minute_bcd, m_last_subq.relative_second_bcd, subq.index_number_bcd, subq.relative_minute_bcd, subq.relative_second_bcd,
m_last_subq.relative_frame_bcd); subq.relative_frame_bcd);
m_async_response_fifo.Clear(); ClearAsyncInterrupt();
m_async_response_fifo.Push(m_secondary_status.bits); m_async_response_fifo.Push(m_secondary_status.bits);
m_async_response_fifo.Push(m_last_subq.track_number_bcd); m_async_response_fifo.Push(subq.track_number_bcd);
m_async_response_fifo.Push(m_last_subq.index_number_bcd); m_async_response_fifo.Push(subq.index_number_bcd);
if (m_last_subq.absolute_frame_bcd & 0x10) if (m_last_subq.absolute_frame_bcd & 0x10)
{ {
m_async_response_fifo.Push(m_last_subq.relative_minute_bcd); m_async_response_fifo.Push(subq.relative_minute_bcd);
m_async_response_fifo.Push(0x80 | m_last_subq.relative_second_bcd); m_async_response_fifo.Push(0x80 | subq.relative_second_bcd);
m_async_response_fifo.Push(m_last_subq.relative_frame_bcd); m_async_response_fifo.Push(subq.relative_frame_bcd);
} }
else else
{ {
m_async_response_fifo.Push(m_last_subq.absolute_minute_bcd); m_async_response_fifo.Push(subq.absolute_minute_bcd);
m_async_response_fifo.Push(m_last_subq.absolute_second_bcd); m_async_response_fifo.Push(subq.absolute_second_bcd);
m_async_response_fifo.Push(m_last_subq.absolute_frame_bcd); m_async_response_fifo.Push(subq.absolute_frame_bcd);
} }
m_async_response_fifo.Push(0); // peak low m_async_response_fifo.Push(0); // peak low
@ -1423,6 +1448,8 @@ void CDROM::ProcessCDDASector(const u8* raw_sector)
} }
} }
m_last_subq = subq;
// Apply volume when pushing sectors to SPU. // Apply volume when pushing sectors to SPU.
if (m_muted) if (m_muted)
return; return;

View file

@ -181,7 +181,7 @@ private:
bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; } bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; }
void SetInterrupt(Interrupt interrupt); void SetInterrupt(Interrupt interrupt);
void SetAsyncInterrupt(Interrupt interrupt); void SetAsyncInterrupt(Interrupt interrupt);
void CancelAsyncInterrupt(); void ClearAsyncInterrupt();
void DeliverAsyncInterrupt(); void DeliverAsyncInterrupt();
void SendACKAndStat(); void SendACKAndStat();
void SendErrorResponse(u8 reason = 0x80); void SendErrorResponse(u8 reason = 0x80);
@ -204,9 +204,9 @@ private:
void DoIDRead(); void DoIDRead();
void DoTOCRead(); void DoTOCRead();
void DoSectorRead(); void DoSectorRead();
void ProcessDataSector(const u8* raw_sector); void ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void ProcessXAADPCMSector(const u8* raw_sector); void ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void ProcessCDDASector(const u8* raw_sector); void ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void BeginSeeking(bool read_after_seek, bool play_after_seek); void BeginSeeking(bool read_after_seek, bool play_after_seek);
void LoadDataFIFO(); void LoadDataFIFO();
@ -245,6 +245,7 @@ private:
CDXA::XASubHeader m_last_sector_subheader{}; CDXA::XASubHeader m_last_sector_subheader{};
CDImage::SubChannelQ m_last_subq{}; CDImage::SubChannelQ m_last_subq{};
u8 m_last_cdda_report_frame_nibble = 0xFF; u8 m_last_cdda_report_frame_nibble = 0xFF;
u8 m_auto_pause_track_number = 0xFF;
std::array<std::array<u8, 2>, 2> m_cd_audio_volume_matrix{}; std::array<std::array<u8, 2>, 2> m_cd_audio_volume_matrix{};
std::array<std::array<u8, 2>, 2> m_next_cd_audio_volume_matrix{}; std::array<std::array<u8, 2>, 2> m_next_cd_audio_volume_matrix{};