Auto repeat CDXA - Game freezes after a while
This commit is contained in:
parent
6ce05d6d4c
commit
97b0dbe007
|
@ -12,9 +12,8 @@ namespace JabyEngine {
|
||||||
static constexpr auto NumberOfWords = BitRange::from_to(0, 15);
|
static constexpr auto NumberOfWords = BitRange::from_to(0, 15);
|
||||||
static constexpr auto CD_OneBlock = Bit(16);
|
static constexpr auto CD_OneBlock = Bit(16);
|
||||||
|
|
||||||
static constexpr BCR for_cd() {
|
static constexpr BCR for_cd(size_t words) {
|
||||||
// v Should be replaced with a named constant
|
return BCR::from(SyncMode0::CD_OneBlock, SyncMode0::NumberOfWords.with(words));
|
||||||
return BCR::from(SyncMode0::CD_OneBlock, SyncMode0::NumberOfWords.with(512));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,17 @@
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
namespace CD {
|
namespace CD {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
enum struct State {
|
||||||
|
Ready = 0,
|
||||||
|
Done = 0,
|
||||||
|
|
||||||
|
XAMode,
|
||||||
|
|
||||||
|
Reading,
|
||||||
|
BufferFull,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
extern State current_state;
|
extern State current_state;
|
||||||
extern uint8_t cmd_interrupt_bit;
|
extern uint8_t cmd_interrupt_bit;
|
||||||
|
|
||||||
|
@ -43,7 +54,7 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_file(AutoLBAEntry file_info, const SectorBufferAllocator& buffer_allocator);
|
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();
|
void continue_reading();
|
||||||
|
|
||||||
CDTimeStamp get_lock();
|
CDTimeStamp get_lock();
|
||||||
|
|
|
@ -6,16 +6,31 @@
|
||||||
|
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
namespace CD {
|
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 {
|
namespace internal {
|
||||||
enum struct State {
|
struct XASectorHeader {
|
||||||
Ready = 0,
|
Header header;
|
||||||
Done = 0,
|
SubHeader sub_header;
|
||||||
|
SubHeader copy_subheader;
|
||||||
XAMode,
|
uint32_t part_data;
|
||||||
|
|
||||||
Reading,
|
|
||||||
BufferFull,
|
|
||||||
Error,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SectorBufferAllocator {
|
class SectorBufferAllocator {
|
||||||
|
|
|
@ -5,16 +5,9 @@ namespace JabyEngine {
|
||||||
namespace CDXA {
|
namespace CDXA {
|
||||||
namespace CD = JabyEngine::CD::internal;
|
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) {
|
void play(const volatile AutoLBAEntry* lba, uint8_t rel_lba_idx, uint8_t channel, bool double_speed) {
|
||||||
|
|
||||||
|
|
||||||
CD::enable_CDXA(double_speed);
|
CD::enable_CDXA(double_speed);
|
||||||
CD::Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::Filter, File, channel);
|
CD::read_xa(lba[rel_lba_idx].get_lba(), channel);
|
||||||
CD::read_xa(lba[rel_lba_idx].get_lba());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
|
@ -22,7 +15,7 @@ namespace JabyEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_channel(uint8_t channel) {
|
void set_channel(uint8_t channel) {
|
||||||
CD::Command::send<CD_IO::PortIndex0>(CD_IO::Command::Filter, File, channel);
|
//CD::Command::send<CD_IO::PortIndex0>(CD_IO::Command::Filter, File, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_play() {
|
void push_play() {
|
||||||
|
|
|
@ -7,31 +7,58 @@
|
||||||
namespace JabyEngine {
|
namespace JabyEngine {
|
||||||
namespace CD {
|
namespace CD {
|
||||||
namespace internal {
|
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 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 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 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();
|
namespace IRQ {
|
||||||
static uint32_t interrupt_handler(uint32_t);
|
static SysCall::InterruptVerifierResult verifier();
|
||||||
|
static uint32_t handler(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
static SectorBufferAllocator sector_allocator;
|
static SectorBufferAllocator sector_allocator;
|
||||||
static uint32_t cur_lba;
|
static Configuration cur_cfg;
|
||||||
static uint32_t dst_lba;
|
|
||||||
|
|
||||||
uint8_t cmd_interrupt_bit = 0;
|
uint8_t cmd_interrupt_bit = 0;
|
||||||
State current_state = State::Ready;
|
State current_state = State::Ready;
|
||||||
SysCall::InterrupCallback callback = {
|
SysCall::InterrupCallback callback = {
|
||||||
.next = nullptr,
|
.next = nullptr,
|
||||||
.handler_function = interrupt_handler,
|
.handler_function = IRQ::handler,
|
||||||
.verifier_function = interrupt_verifier
|
.verifier_function = IRQ::verifier
|
||||||
};
|
};
|
||||||
|
|
||||||
// Requires Index0
|
// 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);
|
const auto loc = CDTimeStamp::from(lba);
|
||||||
|
|
||||||
Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::SetLoc, loc.get_min_cd(), loc.get_sec_cd(), loc.get_sector_cd());
|
Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::SetLoc, loc.get_min_cd(), loc.get_sec_cd(), loc.get_sector_cd());
|
||||||
Command::send<CD_IO::PortIndex0>(desc);
|
Command::send<CD_IO::PortIndex0>(desc);
|
||||||
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_read_n(uint32_t lba) {
|
static void send_read_n(uint32_t lba) {
|
||||||
|
@ -39,129 +66,153 @@ namespace JabyEngine {
|
||||||
current_state = State::Reading;
|
current_state = State::Reading;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_sector_dma(CD_IO::DataSector& sector) {
|
namespace IRQ {
|
||||||
static const auto WaitSectorReady = []() {
|
static void read_sector_dma(uint32_t* dst, size_t bytes) {
|
||||||
while(!CD_IO::IndexStatus.read().is_set(CD_IO::IndexStatus::HasDataFifoData));
|
static const auto WaitSectorReady = []() {
|
||||||
};
|
while(!CD_IO::IndexStatus.read().is_set(CD_IO::IndexStatus::HasDataFifoData));
|
||||||
|
};
|
||||||
|
|
||||||
static const auto ReadSector = [](uint32_t* dst) {
|
static const auto ReadSector = [](uint32_t* dst, size_t bytes) {
|
||||||
DMA_IO::CDROM.set_adr(reinterpret_cast<uintptr_t>(dst));
|
DMA_IO::CDROM.set_adr(reinterpret_cast<uintptr_t>(dst));
|
||||||
DMA_IO::CDROM.block_ctrl.write(DMA_IO::BCR::SyncMode0::for_cd());
|
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.channel_ctrl.write(DMA_IO::CHCHR::StartCDROM());
|
||||||
|
|
||||||
DMA_IO::CDROM.wait();
|
DMA_IO::CDROM.wait();
|
||||||
|
|
||||||
CD_IO::PortIndex0::Request.write(CD_IO::Request::reset());
|
CD_IO::PortIndex0::Request.write(CD_IO::Request::reset());
|
||||||
};
|
};
|
||||||
|
|
||||||
WaitSectorReady();
|
WaitSectorReady();
|
||||||
ReadSector(sector.data);
|
ReadSector(dst, bytes);
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
static void read_sector_to(uint32_t* dst, size_t bytes) {
|
||||||
return SysCall::InterruptVerifierResult::SkipHandler;
|
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 uint32_t interrupt_handler(uint32_t) {
|
static void resume_at(const CDTimeStamp& cd_time) {
|
||||||
const auto old_status = CD_IO::IndexStatus.read();
|
CD_IO::PortIndex0::change_to();
|
||||||
|
Command::send<CD_IO::PortIndex0>(CD_IO::Command::SetLoc, cd_time.get_min_cd(), cd_time.get_sec_cd(), cd_time.get_sector_cd());
|
||||||
|
|
||||||
CD_IO::PortIndex1::change_to();
|
CD_IO::PortIndex1::change_to();
|
||||||
const auto cur_irq = CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag);
|
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::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag);
|
||||||
|
|
||||||
cmd_interrupt_bit = bit::clear(cmd_interrupt_bit, cur_irq);
|
CD_IO::PortIndex0::change_to();
|
||||||
|
}
|
||||||
|
|
||||||
if(current_state != State::XAMode) {
|
//######################################################################################################################
|
||||||
printf("Wuff %i (%i)\n", cur_irq, current_state);
|
|
||||||
|
|
||||||
switch(cur_irq) {
|
static SysCall::InterruptVerifierResult verifier() {
|
||||||
case CD_IO::Interrupt::DataReady: {
|
if(Interrupt::is_irq(Interrupt::CDROM)) {
|
||||||
// Obtain sector content here
|
return SysCall::InterruptVerifierResult::ExecuteHandler;
|
||||||
auto* sector = sector_allocator.allocate_sector();
|
}
|
||||||
if(sector) {
|
|
||||||
//Now obtain sector
|
|
||||||
read_sector_to(*sector);
|
|
||||||
|
|
||||||
cur_lba++;
|
else {
|
||||||
if(cur_lba == dst_lba) {
|
return SysCall::InterruptVerifierResult::SkipHandler;
|
||||||
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::PortIndex0>(CD_IO::Command::SetLoc, static_cast<uint8_t>(0x0), static_cast<uint8_t>(0x09), static_cast<uint8_t>(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::PortIndex0>(CD_IO::Command::Play);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case CD_IO::Interrupt::DiskError: {
|
|
||||||
current_state = State::Error;
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
static uint32_t handler(uint32_t) {
|
||||||
printf("Playing CDXA\n");
|
const auto old_status = CD_IO::IndexStatus.read();
|
||||||
}
|
|
||||||
|
|
||||||
// No masking required because we can only write bit 0 - 2
|
CD_IO::PortIndex1::change_to();
|
||||||
CD_IO::IndexStatus.write(old_status);
|
const auto cur_irq = CD_IO::Interrupt::get_type(CD_IO::PortIndex1::InterruptFlag);
|
||||||
Interrupt::ack_irq(Interrupt::CDROM);
|
CD_IO::Interrupt::ack_extended(CD_IO::PortIndex1::InterruptFlag);
|
||||||
SysCall::ReturnFromException();
|
|
||||||
__builtin_unreachable();
|
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::PortIndex0>(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<uint32_t*>(&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::PortIndex0>(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) {
|
void read_file(AutoLBAEntry file_info, const SectorBufferAllocator& buffer_allocator) {
|
||||||
cur_lba = file_info.get_lba();
|
cur_cfg.file.set_from(file_info);
|
||||||
dst_lba = cur_lba + file_info.get_size_in_sectors();
|
|
||||||
sector_allocator = buffer_allocator;
|
sector_allocator = buffer_allocator;
|
||||||
|
|
||||||
Command::wait_completed();
|
Command::wait_completed();
|
||||||
CD_IO::PortIndex0::change_to();
|
CD_IO::PortIndex0::change_to();
|
||||||
Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::SetMode, DataSectorMode);
|
Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::SetMode, DataSectorMode);
|
||||||
|
|
||||||
send_read_n(cur_lba);
|
send_read_n(cur_cfg.file.cur_lba);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_xa(uint32_t lba) {
|
void read_xa(uint32_t lba, uint8_t channel) {
|
||||||
send_read_cmd(lba, CD_IO::Command::ReadS);
|
static constexpr uint8_t File = 1;
|
||||||
|
|
||||||
|
cur_cfg.xa.channel = channel;
|
||||||
|
Command::send_wait<CD_IO::PortIndex0>(CD_IO::Command::Filter, File, channel);
|
||||||
|
|
||||||
|
cur_cfg.xa.start_time = send_read_cmd(lba, CD_IO::Command::ReadS);
|
||||||
current_state = State::XAMode;
|
current_state = State::XAMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void continue_reading() {
|
void continue_reading() {
|
||||||
if(current_state == State::BufferFull) {
|
if(current_state == State::BufferFull) {
|
||||||
Command::wait_completed();
|
Command::wait_completed();
|
||||||
send_read_n(cur_lba);
|
send_read_n(cur_cfg.file.cur_lba);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue