Skip to content

Commit

Permalink
Changing strategy for async cdrom calls.
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasnoble committed Oct 1, 2024
1 parent fd1bddf commit 5d9e150
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 70 deletions.
42 changes: 23 additions & 19 deletions src/mips/psyqo/cdrom-device.hh
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ class CDRomDevice final : public CDRom {
* stop the CDRom drive motor, which means that another `playCDDA`
* call start playing faster than if the motor was stopped.
*/
static void pauseCDDA() { __asm__ volatile("break 14, 1\n"); }
void pauseCDDA();

/**
* @brief Stops CDDA playback.
Expand All @@ -255,7 +255,7 @@ class CDRomDevice final : public CDRom {
* the CDRom drive motor, which means that another `playCDDA` call
* will take longer to start playing than if the motor was not stopped.
*/
static void stopCDDA() { __asm__ volatile("break 14, 2\n"); }
void stopCDDA();

/**
* @brief Get the Playback location of the CDDA audio.
Expand All @@ -278,22 +278,22 @@ class CDRomDevice final : public CDRom {
* @brief Set the Volume of the CDDA audio.
*
* @details This method will set the volume of the CDDA audio. The
* volume is set using four values, which represent the volume of
* the left channel to the left speaker, the right channel to the
* left speaker, the left channel to the right speaker, and the
* right channel to the right speaker. The given output value
* should be in the range of 0 to 128, where 0 is silence and 128
* is full volume. The values for a given output speaker will be
* added together, so clipping can occur if the sum of the values
* is greater than 128. The method can be used at any time, unlike
* the mute/unmute methods, which can only be used when the drive
* is idle. The normal volume setting is 0x80, 0x00, 0x00, 0x80.
*
* @param leftToLeft The volume of the left channel to the left speaker.
* @param rightToLeft The volume of the right channel to the left speaker.
* @param leftToRight The volume of the left channel to the right speaker.
* @param rightToRight The volume of the right channel to the right speaker.
*/
* volume is set using four values, which represent the volume of
* the left channel to the left speaker, the right channel to the
* left speaker, the left channel to the right speaker, and the
* right channel to the right speaker. The given output value
* should be in the range of 0 to 128, where 0 is silence and 128
* is full volume. The values for a given output speaker will be
* added together, so clipping can occur if the sum of the values
* is greater than 128. The method can be used at any time, unlike
* the mute/unmute methods, which can only be used when the drive
* is idle. The normal volume setting is 0x80, 0x00, 0x00, 0x80.
*
* @param leftToLeft The volume of the left channel to the left speaker.
* @param rightToLeft The volume of the right channel to the left speaker.
* @param leftToRight The volume of the left channel to the right speaker.
* @param rightToRight The volume of the right channel to the right speaker.
*/
void setVolume(uint8_t leftToLeft, uint8_t rightToLeft, uint8_t leftToRight, uint8_t rightToRight);

/**
Expand Down Expand Up @@ -336,7 +336,6 @@ class CDRomDevice final : public CDRom {
bool m_success = false;
bool m_blocking = false;
bool m_pendingGetLocation = false;
uint8_t m_leftToLeft, m_rightToLeft, m_leftToRight, m_rightToRight;

struct BlockingAction {
BlockingAction(CDRomDevice *, GPU &);
Expand All @@ -346,6 +345,11 @@ class CDRomDevice final : public CDRom {
CDRomDevice *m_device;
GPU &m_gpu;
};

struct MaskedIRQ {
MaskedIRQ();
~MaskedIRQ();
};
};

} // namespace psyqo
57 changes: 50 additions & 7 deletions src/mips/psyqo/src/cdrom-device-cdda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,61 @@ void psyqo::CDRomDevice::resumeCDDA(eastl::function<void(bool)> &&callback) {
s_playCDDAAction.start(this, eastl::move(callback));
}

void psyqo::CDRomDevice::pauseCDDA() {
MaskedIRQ masked;
// If no action is in progress, it means we got
// raced to the end of the track and/or disc, and
// we should just ignore this.
if (m_action == nullptr) return;
// If we aren't actually playing audio, that's
// a fatal error.
Kernel::assert(m_state == 100, "CDRomDevice::pauseCDDA called while not playing");
m_state = 101;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(Hardware::CDRom::CDL::PAUSE);
}

void psyqo::CDRomDevice::stopCDDA() {
MaskedIRQ masked;
// If no action is in progress, it means we got
// raced to the end of the track and/or disc, and
// we should just ignore this.
if (m_action == nullptr) return;
// If we aren't actually playing audio, that's
// a fatal error.
Kernel::assert(m_state == 100, "CDRomDevice::stopCDDA called while not playing");
m_state = 101;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(Hardware::CDRom::CDL::STOP);
}

void psyqo::CDRomDevice::getPlaybackLocation(eastl::function<void(PlaybackLocation *)> &&callback) {
MaskedIRQ masked;
Kernel::assert(m_locationCallback == nullptr, "CDRomDevice::getPlaybackLocation while another one is pending");
if (m_action == nullptr) {
Kernel::queueCallbackFromISR([callback = eastl::move(callback)]() { callback(nullptr); });
return;
}
m_locationCallback = eastl::move(callback);
m_locationPtr = &m_locationStorage;
__asm__ volatile("break 14, 3");
m_pendingGetLocation = true;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(Hardware::CDRom::CDL::GETLOCP);
}

void psyqo::CDRomDevice::getPlaybackLocation(PlaybackLocation *location,
eastl::function<void(PlaybackLocation *)> &&callback) {
MaskedIRQ masked;
Kernel::assert(m_locationCallback == nullptr, "CDRomDevice::getPlaybackLocation while another one is pending");
if (m_action == nullptr) {
Kernel::queueCallbackFromISR([callback = eastl::move(callback)]() { callback(nullptr); });
return;
}
m_locationCallback = eastl::move(callback);
m_locationPtr = location ? location : &m_locationStorage;
__asm__ volatile("break 14, 3");
m_pendingGetLocation = true;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(Hardware::CDRom::CDL::GETLOCP);
}

psyqo::TaskQueue::Task psyqo::CDRomDevice::scheduleGetPlaybackLocation(PlaybackLocation *location) {
Expand All @@ -229,9 +271,10 @@ void psyqo::CDRomDevice::ActionBase::queueGetLocationCallback(bool success) {
}

void psyqo::CDRomDevice::setVolume(uint8_t leftToLeft, uint8_t rightToLeft, uint8_t leftToRight, uint8_t rightToRight) {
m_leftToLeft = leftToLeft;
m_rightToLeft = rightToLeft;
m_leftToRight = leftToRight;
m_rightToRight = rightToRight;
__asm__ volatile("break 14, 4");
MaskedIRQ masked;
Hardware::CDRom::LeftToLeftVolume = leftToLeft;
Hardware::CDRom::RightToLeftVolume = rightToLeft;
Hardware::CDRom::LeftToRightVolume = leftToRight;
Hardware::CDRom::RightToRightVolume = rightToRight;
Hardware::CDRom::VolumeSettings = 0x20;
}
51 changes: 7 additions & 44 deletions src/mips/psyqo/src/cdrom-device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,50 +48,6 @@ void psyqo::CDRomDevice::prepare() {
m_event = Kernel::openEvent(EVENT_CDROM, 0x1000, EVENT_MODE_CALLBACK, eastl::move(callback));
syscall_enableEvent(m_event);
}
Kernel::queuePsyqoBreakHandler([this](uint32_t code) {
switch (code) {
// Pause CDDA playback
case 1:
// Stop CDDA playback
case 2:
// If no action is in progress, it means we got
// raced to the end of the track and/or disc, and
// we should just ignore this.
if (m_action == nullptr) return true;
// If we aren't actually playing audio, that's
// a fatal error.
if (m_state != 100) return false;
m_state = 101;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(code == 1 ? Hardware::CDRom::CDL::PAUSE : Hardware::CDRom::CDL::STOP);
return true;
// Get playback location
case 3:
// We got raced to the end of the track and/or disc, and we can't
// properly handle this request. Just ignore it and let the caller
// retry if they want to.
if (m_action == nullptr) {
Kernel::queueCallbackFromISR([callback = eastl::move(m_locationCallback)]() { callback(nullptr); });
return true;
}
m_pendingGetLocation = true;
eastl::atomic_signal_fence(eastl::memory_order_release);
Hardware::CDRom::Command.send(Hardware::CDRom::CDL::GETLOCP);
return true;
// Set CDDA volume
case 4:
// This needs to be done in the interrupt handler, so we
// don't mess up the internal cdrom state machine if we
// happen to get an IRQ while changing these registers.
Hardware::CDRom::LeftToLeftVolume = m_leftToLeft;
Hardware::CDRom::RightToLeftVolume = m_rightToLeft;
Hardware::CDRom::LeftToRightVolume = m_leftToRight;
Hardware::CDRom::RightToRightVolume = m_rightToRight;
Hardware::CDRom::VolumeSettings = 0x20;
return true;
}
return false;
});
setVolume(0x80, 0x00, 0x00, 0x80);
}

Expand Down Expand Up @@ -208,6 +164,13 @@ psyqo::CDRomDevice::BlockingAction::~BlockingAction() {
Hardware::CPU::IMask.set(Hardware::CPU::IRQ::CDRom);
}

psyqo::CDRomDevice::MaskedIRQ::MaskedIRQ() {
Hardware::CPU::IMask.clear(Hardware::CPU::IRQ::CDRom);
Hardware::CPU::flushWriteQueue();
}

psyqo::CDRomDevice::MaskedIRQ::~MaskedIRQ() { Hardware::CPU::IMask.set(Hardware::CPU::IRQ::CDRom); }

void psyqo::CDRomDevice::actionComplete() {
auto callback = eastl::move(m_callback);
m_callback = nullptr;
Expand Down

0 comments on commit 5d9e150

Please sign in to comment.