From 30495279eb34290fc96d4e388279e50505d043e6 Mon Sep 17 00:00:00 2001 From: jaby Date: Wed, 29 May 2024 20:33:18 +0200 Subject: [PATCH] Auto repeat CDXA - Game freezes after a while --- include/PSX/System/IOPorts/dma_io.hpp | 5 +- .../internal-include/CD/cd_internal.hpp | 13 +- src/Library/internal-include/CD/cd_types.hpp | 33 ++- src/Library/src/Audio/CDXA.cpp | 11 +- src/Library/src/CD/cd.cpp | 265 +++++++++++------- 5 files changed, 198 insertions(+), 129 deletions(-) diff --git a/include/PSX/System/IOPorts/dma_io.hpp b/include/PSX/System/IOPorts/dma_io.hpp index 8d41f267..f13576e9 100644 --- a/include/PSX/System/IOPorts/dma_io.hpp +++ b/include/PSX/System/IOPorts/dma_io.hpp @@ -12,9 +12,8 @@ namespace JabyEngine { static constexpr auto NumberOfWords = BitRange::from_to(0, 15); static constexpr auto CD_OneBlock = Bit(16); - static constexpr BCR for_cd() { - // v Should be replaced with a named constant - return BCR::from(SyncMode0::CD_OneBlock, SyncMode0::NumberOfWords.with(512)); + static constexpr BCR for_cd(size_t words) { + return BCR::from(SyncMode0::CD_OneBlock, SyncMode0::NumberOfWords.with(words)); } }; diff --git a/src/Library/internal-include/CD/cd_internal.hpp b/src/Library/internal-include/CD/cd_internal.hpp index 640f8c2f..ad9d2924 100644 --- a/src/Library/internal-include/CD/cd_internal.hpp +++ b/src/Library/internal-include/CD/cd_internal.hpp @@ -4,6 +4,17 @@ namespace JabyEngine { namespace CD { namespace internal { + enum struct State { + Ready = 0, + Done = 0, + + XAMode, + + Reading, + BufferFull, + Error, + }; + extern State current_state; extern uint8_t cmd_interrupt_bit; @@ -43,7 +54,7 @@ namespace JabyEngine { } void read_file(AutoLBAEntry file_info, const SectorBufferAllocator& buffer_allocator); - void read_xa(uint32_t lba); + void read_xa(uint32_t lba, uint8_t channel); void continue_reading(); CDTimeStamp get_lock(); diff --git a/src/Library/internal-include/CD/cd_types.hpp b/src/Library/internal-include/CD/cd_types.hpp index 499fef17..f3f6aa31 100644 --- a/src/Library/internal-include/CD/cd_types.hpp +++ b/src/Library/internal-include/CD/cd_types.hpp @@ -6,16 +6,31 @@ namespace JabyEngine { namespace CD { + using BCD = uint8_t; + using CodingInfo = uint8_t; + using Mode = uint8_t; + using SubMode = uint8_t; + + struct Header { + BCD minute; + BCD second; + BCD sector; + Mode mode; + }; + + struct SubHeader { + uint8_t file_number; + uint8_t channel_number; + SubMode sub_mode; + CodingInfo coding_info; + }; + namespace internal { - enum struct State { - Ready = 0, - Done = 0, - - XAMode, - - Reading, - BufferFull, - Error, + struct XASectorHeader { + Header header; + SubHeader sub_header; + SubHeader copy_subheader; + uint32_t part_data; }; class SectorBufferAllocator { diff --git a/src/Library/src/Audio/CDXA.cpp b/src/Library/src/Audio/CDXA.cpp index eecf2780..80d8cdc5 100644 --- a/src/Library/src/Audio/CDXA.cpp +++ b/src/Library/src/Audio/CDXA.cpp @@ -4,17 +4,10 @@ namespace JabyEngine { namespace CDXA { namespace CD = JabyEngine::CD::internal; - - static constexpr uint8_t File = 1; - - static CD::CDTimeStamp last_track; void play(const volatile AutoLBAEntry* lba, uint8_t rel_lba_idx, uint8_t channel, bool double_speed) { - - CD::enable_CDXA(double_speed); - CD::Command::send_wait(CD_IO::Command::Filter, File, channel); - CD::read_xa(lba[rel_lba_idx].get_lba()); + CD::read_xa(lba[rel_lba_idx].get_lba(), channel); } void stop() { @@ -22,7 +15,7 @@ namespace JabyEngine { } void set_channel(uint8_t channel) { - CD::Command::send(CD_IO::Command::Filter, File, channel); + //CD::Command::send(CD_IO::Command::Filter, File, channel); } void push_play() { diff --git a/src/Library/src/CD/cd.cpp b/src/Library/src/CD/cd.cpp index 7147a5ae..9a8bd3a5 100644 --- a/src/Library/src/CD/cd.cpp +++ b/src/Library/src/CD/cd.cpp @@ -7,31 +7,58 @@ namespace JabyEngine { namespace CD { namespace internal { + union Configuration { + struct File { + uint32_t cur_lba; + uint32_t dst_lba; + + void set_from(const AutoLBAEntry& file_info) { + this->cur_lba = file_info.get_lba(); + this->dst_lba = this->cur_lba + file_info.get_size_in_sectors(); + } + + bool done_processing() { + this->cur_lba++; + return this->cur_lba == this->dst_lba; + } + }; + + struct XA { + CDTimeStamp start_time; + uint8_t channel; + }; + + File file; + XA xa; + }; + static constexpr auto AudioSectorMode = CD_IO::Mode::from(CD_IO::Mode::SingleSpeed, CD_IO::Mode::AutoPauseTrack, CD_IO::Mode::CDDA); static constexpr auto DataSectorMode = CD_IO::Mode::from(CD_IO::Mode::DoubleSpeed, CD_IO::Mode::DataSector); static constexpr auto XAAudioSectorMode = CD_IO::Mode::from(CD_IO::Mode::SingleSpeed, CD_IO::Mode::XADPCM, CD_IO::Mode::WholeSector, CD_IO::Mode::UseXAFilter); - static SysCall::InterruptVerifierResult interrupt_verifier(); - static uint32_t interrupt_handler(uint32_t); + namespace IRQ { + static SysCall::InterruptVerifierResult verifier(); + static uint32_t handler(uint32_t); + } static SectorBufferAllocator sector_allocator; - static uint32_t cur_lba; - static uint32_t dst_lba; + static Configuration cur_cfg; uint8_t cmd_interrupt_bit = 0; State current_state = State::Ready; SysCall::InterrupCallback callback = { .next = nullptr, - .handler_function = interrupt_handler, - .verifier_function = interrupt_verifier + .handler_function = IRQ::handler, + .verifier_function = IRQ::verifier }; // Requires Index0 - static void send_read_cmd(uint32_t lba, CD_IO::Command::Desc desc) { + static CDTimeStamp send_read_cmd(uint32_t lba, CD_IO::Command::Desc desc) { const auto loc = CDTimeStamp::from(lba); Command::send_wait(CD_IO::Command::SetLoc, loc.get_min_cd(), loc.get_sec_cd(), loc.get_sector_cd()); Command::send(desc); + return loc; } static void send_read_n(uint32_t lba) { @@ -39,129 +66,153 @@ namespace JabyEngine { current_state = State::Reading; } - static void read_sector_dma(CD_IO::DataSector& sector) { - static const auto WaitSectorReady = []() { - while(!CD_IO::IndexStatus.read().is_set(CD_IO::IndexStatus::HasDataFifoData)); - }; + namespace IRQ { + static void read_sector_dma(uint32_t* dst, size_t bytes) { + static const auto WaitSectorReady = []() { + while(!CD_IO::IndexStatus.read().is_set(CD_IO::IndexStatus::HasDataFifoData)); + }; - static const auto ReadSector = [](uint32_t* dst) { - DMA_IO::CDROM.set_adr(reinterpret_cast(dst)); - DMA_IO::CDROM.block_ctrl.write(DMA_IO::BCR::SyncMode0::for_cd()); - DMA_IO::CDROM.channel_ctrl.write(DMA_IO::CHCHR::StartCDROM()); + static const auto ReadSector = [](uint32_t* dst, size_t bytes) { + DMA_IO::CDROM.set_adr(reinterpret_cast(dst)); + DMA_IO::CDROM.block_ctrl.write(DMA_IO::BCR::SyncMode0::for_cd(bytes >> 2)); + DMA_IO::CDROM.channel_ctrl.write(DMA_IO::CHCHR::StartCDROM()); + + DMA_IO::CDROM.wait(); + + CD_IO::PortIndex0::Request.write(CD_IO::Request::reset()); + }; + + WaitSectorReady(); + ReadSector(dst, bytes); + } + + static void read_sector_to(uint32_t* dst, size_t bytes) { + CD_IO::PortIndex0::change_to(); + CD_IO::PortIndex0::Request.write(CD_IO::Request::want_data()); + + // We only support DMA rn + read_sector_dma(dst, bytes); + + // Do we ever want to support reading via IO Port? + // Doesn't seem to important when we can use DMA + } + + static void resume_at(const CDTimeStamp& cd_time) { + CD_IO::PortIndex0::change_to(); + Command::send(CD_IO::Command::SetLoc, cd_time.get_min_cd(), cd_time.get_sec_cd(), cd_time.get_sector_cd()); + + CD_IO::PortIndex1::change_to(); + while(CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag) != CD_IO::Interrupt::Acknowledge); + CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag); + + CD_IO::PortIndex0::change_to(); + } + + //###################################################################################################################### + + static SysCall::InterruptVerifierResult verifier() { + if(Interrupt::is_irq(Interrupt::CDROM)) { + return SysCall::InterruptVerifierResult::ExecuteHandler; + } - DMA_IO::CDROM.wait(); - - CD_IO::PortIndex0::Request.write(CD_IO::Request::reset()); - }; - - WaitSectorReady(); - ReadSector(sector.data); - } - - static void read_sector_to(CD_IO::DataSector& sector) { - CD_IO::PortIndex0::change_to(); - CD_IO::PortIndex0::Request.write(CD_IO::Request::want_data()); - - // We only support DMA rn - read_sector_dma(sector); - - // Do we ever want to support reading via IO Port? - // Doesn't seem to important when we can use DMA - } - - static SysCall::InterruptVerifierResult interrupt_verifier() { - if(Interrupt::is_irq(Interrupt::CDROM)) { - return SysCall::InterruptVerifierResult::ExecuteHandler; - } - - else { - return SysCall::InterruptVerifierResult::SkipHandler; - } - } - - static uint32_t interrupt_handler(uint32_t) { - const auto old_status = CD_IO::IndexStatus.read(); - - CD_IO::PortIndex1::change_to(); - const auto cur_irq = CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag); - CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag); - - cmd_interrupt_bit = bit::clear(cmd_interrupt_bit, cur_irq); - - if(current_state != State::XAMode) { - printf("Wuff %i (%i)\n", cur_irq, current_state); - - switch(cur_irq) { - case CD_IO::Interrupt::DataReady: { - // Obtain sector content here - auto* sector = sector_allocator.allocate_sector(); - if(sector) { - //Now obtain sector - read_sector_to(*sector); - - cur_lba++; - if(cur_lba == dst_lba) { - current_state = State::Done; - pause(); - } - } - - else { - current_state = State::BufferFull; - pause(); - } - } break; - - case CD_IO::Interrupt::DataEnd: { - CD_IO::PortIndex0::change_to(); - Command::send(CD_IO::Command::SetLoc, static_cast(0x0), static_cast(0x09), static_cast(0x0)); - - CD_IO::PortIndex1::change_to(); - while(CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag) != CD_IO::Interrupt::Acknowledge); - CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag); - - CD_IO::PortIndex0::change_to(); - Command::send(CD_IO::Command::Play); - } break; - - case CD_IO::Interrupt::DiskError: { - current_state = State::Error; - } break; + else { + return SysCall::InterruptVerifierResult::SkipHandler; } } - else { - printf("Playing CDXA\n"); - } + static uint32_t handler(uint32_t) { + const auto old_status = CD_IO::IndexStatus.read(); - // No masking required because we can only write bit 0 - 2 - CD_IO::IndexStatus.write(old_status); - Interrupt::ack_irq(Interrupt::CDROM); - SysCall::ReturnFromException(); - __builtin_unreachable(); + CD_IO::PortIndex1::change_to(); + const auto cur_irq = CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag); + CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag); + + cmd_interrupt_bit = bit::clear(cmd_interrupt_bit, cur_irq); + + if(current_state != State::XAMode) { + switch(cur_irq) { + case CD_IO::Interrupt::DataReady: { + // Obtain sector content here + auto* sector = sector_allocator.allocate_sector(); + if(sector) { + //Now obtain sector + read_sector_to(sector->data, CD_IO::DataSector::SizeBytes); + + if(cur_cfg.file.done_processing()) { + current_state = State::Done; + pause(); + } + } + + else { + current_state = State::BufferFull; + pause(); + } + } break; + + case CD_IO::Interrupt::DataEnd: { + // TODO: Fix this!! This is a freaking static time + resume_at(CDTimeStamp{.min = 0, .sec = 9, .sector = 0}); + Command::send(CD_IO::Command::Play); + } break; + + case CD_IO::Interrupt::DiskError: { + current_state = State::Error; + } break; + } + } + + else { + switch(cur_irq) { + case CD_IO::Interrupt::DataReady: { + XASectorHeader xa_file; + + read_sector_to(reinterpret_cast(&xa_file), sizeof(XASectorHeader)); + if(cur_cfg.xa.channel == xa_file.sub_header.channel_number) { + resume_at(cur_cfg.xa.start_time); + Command::send(CD_IO::Command::ReadS); + } + } break; + + case CD_IO::Interrupt::DiskError: { + current_state = State::Error; + } break; + }; + } + + // No masking required because we can only write bit 0 - 2 + CD_IO::IndexStatus.write(old_status); + Interrupt::ack_irq(Interrupt::CDROM); + SysCall::ReturnFromException(); + __builtin_unreachable(); + } } void read_file(AutoLBAEntry file_info, const SectorBufferAllocator& buffer_allocator) { - cur_lba = file_info.get_lba(); - dst_lba = cur_lba + file_info.get_size_in_sectors(); + cur_cfg.file.set_from(file_info); sector_allocator = buffer_allocator; Command::wait_completed(); CD_IO::PortIndex0::change_to(); Command::send_wait(CD_IO::Command::SetMode, DataSectorMode); - send_read_n(cur_lba); + send_read_n(cur_cfg.file.cur_lba); } - void read_xa(uint32_t lba) { - send_read_cmd(lba, CD_IO::Command::ReadS); + void read_xa(uint32_t lba, uint8_t channel) { + static constexpr uint8_t File = 1; + + cur_cfg.xa.channel = channel; + Command::send_wait(CD_IO::Command::Filter, File, channel); + + cur_cfg.xa.start_time = send_read_cmd(lba, CD_IO::Command::ReadS); current_state = State::XAMode; } void continue_reading() { if(current_state == State::BufferFull) { Command::wait_completed(); - send_read_n(cur_lba); + send_read_n(cur_cfg.file.cur_lba); } }