CDROM: Implement auto-pause at end of track
This commit is contained in:
parent
85413218cb
commit
8e06f86db8
|
@ -142,6 +142,12 @@ public:
|
|||
u8 data[SUBCHANNEL_BYTES_PER_FRAME];
|
||||
|
||||
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");
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ bool CDROM::DoState(StateWrapper& sw)
|
|||
sw.DoBytes(&m_last_sector_subheader, sizeof(m_last_sector_subheader));
|
||||
sw.DoBytes(&m_last_subq, sizeof(m_last_subq));
|
||||
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_next_cd_audio_volume_matrix);
|
||||
sw.Do(&m_xa_last_samples);
|
||||
|
@ -432,7 +433,7 @@ void CDROM::SetAsyncInterrupt(Interrupt interrupt)
|
|||
DeliverAsyncInterrupt();
|
||||
}
|
||||
|
||||
void CDROM::CancelAsyncInterrupt()
|
||||
void CDROM::ClearAsyncInterrupt()
|
||||
{
|
||||
m_pending_async_interrupt = 0;
|
||||
m_async_response_fifo.Clear();
|
||||
|
@ -1046,6 +1047,7 @@ void CDROM::BeginReading(bool cdda)
|
|||
m_sector_buffer.clear();
|
||||
|
||||
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_remaining_ticks = GetTicksForRead();
|
||||
|
@ -1175,14 +1177,33 @@ void CDROM::DoTOCRead()
|
|||
void CDROM::DoSectorRead()
|
||||
{
|
||||
// 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];
|
||||
if (!m_media->ReadSubChannelQ(&m_last_subq) || !m_media->ReadRawSector(raw_sector))
|
||||
if (!m_media->ReadRawSector(raw_sector))
|
||||
Panic("Sector read failed");
|
||||
|
||||
if (m_drive_state == DriveState::Reading)
|
||||
ProcessDataSector(raw_sector);
|
||||
ProcessDataSector(raw_sector, subq);
|
||||
else if (m_drive_state == DriveState::Playing)
|
||||
ProcessCDDASector(raw_sector);
|
||||
ProcessCDDASector(raw_sector, subq);
|
||||
else
|
||||
Panic("Not reading or playing");
|
||||
|
||||
|
@ -1190,11 +1211,15 @@ void CDROM::DoSectorRead()
|
|||
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_subheader, &raw_sector[SECTOR_SYNC_SIZE + sizeof(m_last_sector_header)],
|
||||
sizeof(m_last_sector_subheader));
|
||||
|
||||
// TODO: Check SubQ checksum.
|
||||
m_last_subq = subq;
|
||||
|
||||
m_secondary_status.header_valid = true;
|
||||
|
||||
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
|
||||
{
|
||||
ProcessXAADPCMSector(raw_sector);
|
||||
ProcessXAADPCMSector(raw_sector, subq);
|
||||
}
|
||||
|
||||
// Audio+realtime sectors aren't delivered to the CPU.
|
||||
|
@ -1231,7 +1256,7 @@ void CDROM::ProcessDataSector(const u8* raw_sector)
|
|||
if (HasPendingAsyncInterrupt())
|
||||
{
|
||||
Log_WarningPrintf("Data interrupt was not delivered");
|
||||
CancelAsyncInterrupt();
|
||||
ClearAsyncInterrupt();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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.
|
||||
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;
|
||||
|
||||
Log_DebugPrintf("CDDA report at track[%02x] index[%02x] rel[%02x:%02x:%02x]", m_last_subq.track_number_bcd,
|
||||
m_last_subq.index_number_bcd, m_last_subq.relative_minute_bcd, m_last_subq.relative_second_bcd,
|
||||
m_last_subq.relative_frame_bcd);
|
||||
Log_DebugPrintf("CDDA report at track[%02x] index[%02x] rel[%02x:%02x:%02x]", subq.track_number_bcd,
|
||||
subq.index_number_bcd, subq.relative_minute_bcd, subq.relative_second_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_last_subq.track_number_bcd);
|
||||
m_async_response_fifo.Push(m_last_subq.index_number_bcd);
|
||||
m_async_response_fifo.Push(subq.track_number_bcd);
|
||||
m_async_response_fifo.Push(subq.index_number_bcd);
|
||||
if (m_last_subq.absolute_frame_bcd & 0x10)
|
||||
{
|
||||
m_async_response_fifo.Push(m_last_subq.relative_minute_bcd);
|
||||
m_async_response_fifo.Push(0x80 | m_last_subq.relative_second_bcd);
|
||||
m_async_response_fifo.Push(m_last_subq.relative_frame_bcd);
|
||||
m_async_response_fifo.Push(subq.relative_minute_bcd);
|
||||
m_async_response_fifo.Push(0x80 | subq.relative_second_bcd);
|
||||
m_async_response_fifo.Push(subq.relative_frame_bcd);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_async_response_fifo.Push(m_last_subq.absolute_minute_bcd);
|
||||
m_async_response_fifo.Push(m_last_subq.absolute_second_bcd);
|
||||
m_async_response_fifo.Push(m_last_subq.absolute_frame_bcd);
|
||||
m_async_response_fifo.Push(subq.absolute_minute_bcd);
|
||||
m_async_response_fifo.Push(subq.absolute_second_bcd);
|
||||
m_async_response_fifo.Push(subq.absolute_frame_bcd);
|
||||
}
|
||||
|
||||
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.
|
||||
if (m_muted)
|
||||
return;
|
||||
|
|
|
@ -181,7 +181,7 @@ private:
|
|||
bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; }
|
||||
void SetInterrupt(Interrupt interrupt);
|
||||
void SetAsyncInterrupt(Interrupt interrupt);
|
||||
void CancelAsyncInterrupt();
|
||||
void ClearAsyncInterrupt();
|
||||
void DeliverAsyncInterrupt();
|
||||
void SendACKAndStat();
|
||||
void SendErrorResponse(u8 reason = 0x80);
|
||||
|
@ -204,9 +204,9 @@ private:
|
|||
void DoIDRead();
|
||||
void DoTOCRead();
|
||||
void DoSectorRead();
|
||||
void ProcessDataSector(const u8* raw_sector);
|
||||
void ProcessXAADPCMSector(const u8* raw_sector);
|
||||
void ProcessCDDASector(const u8* raw_sector);
|
||||
void ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
||||
void ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
||||
void ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
||||
void BeginSeeking(bool read_after_seek, bool play_after_seek);
|
||||
void LoadDataFIFO();
|
||||
|
||||
|
@ -245,6 +245,7 @@ private:
|
|||
CDXA::XASubHeader m_last_sector_subheader{};
|
||||
CDImage::SubChannelQ m_last_subq{};
|
||||
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_next_cd_audio_volume_matrix{};
|
||||
|
|
Loading…
Reference in New Issue