Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

Engine

Struct Engine 

Source
pub struct Engine<H: CdHw> { /* private fields */ }
Expand description

The CD transport.

One engine owns the drive. Foreground code submits Requests; the CD interrupt handler calls Engine::on_interrupt once per interrupt, and each call advances the machine by one controller event. Nothing in the machine waits for the drive: a command is written and the machine returns, and the response arrives as the next interrupt.

§A transfer

Setloc ──INT3──▶ SeekL ──INT3──▶ (seek) ──INT2──▶ Setmode(double speed)
  ──INT3──▶ ReadN ──INT3──▶ INT1 × n (one sector each) ──▶ Pause
  ──INT3──▶ (stop) ──INT2──▶ stopped

The read is seek-first (Setloc, SeekL, then Setmode and ReadN), the form the BIOS uses and the only one proven to stream byte-perfect on a console: a bare Setloc plus ReadN starts delivering while the mechanism is still settling. Each INT1 pops one sector into the request’s destination. After a failure the drive’s state is unknown, so the next transfer opens with a recovery Pause.

§Chaining

When the last sector of a request lands and the best queued request starts at the next sector, the machine swaps the destination inside that same interrupt and keeps reading: no Pause, no seek. A request is Done the moment its last sector lands, even though the drive is still running on to the Pause; Engine::is_idle says when it has stopped.

§Abort

Engine::cancel only sets a flag. The handler drops the next sector it pops and issues Pause itself. Pausing from the foreground while sectors keep arriving lost the Pause acknowledge on silicon (no interrupt for 600 VBlanks), so the foreground never does it.

§Generic over the drive

On the console the one instance lives in the crate’s global and is reached through the free functions at the crate root; on the host a scripted drive stands in.

Implementations§

Source§

impl<H: CdHw> Engine<H>

Source

pub const fn new(hw: H, config: Config) -> Self

An engine in the Owner::Boot state: nothing runs until attach.

Source

pub fn configure(&mut self, config: Config)

Replace the configuration. It applies from the next event.

Source

pub fn hw(&self) -> &H

The drive layer.

Source

pub fn hw_mut(&mut self) -> &mut H

The drive layer, mutably.

Source

pub fn owner(&self) -> Owner

Who may program the controller.

Source

pub fn phase(&self) -> Phase

The controller phase.

Source

pub fn stats(&self) -> StreamStats

A copy of the counters, with the current phase and error filled in.

Source

pub fn reset_max_irq_ticks(&mut self)

Start the longest-handler measurement over (StreamStats::max_irq_ticks to 0), to attribute the peak to the next stretch of work.

Source

pub fn queued_count(&self) -> usize

Requests waiting behind the active one.

Source

pub fn is_idle(&self) -> bool

Whether the drive is stopped and no request holds it. Queued requests may still be waiting for their turn (while the owner is not Data).

Source

pub fn attach(&mut self)

Take the drive: Owner::Boot becomes Owner::Data and queued requests start.

The controller must be in a clean state: the polled reader released, no command in flight. A no-op in any other state.

Source

pub fn detach(&mut self) -> bool

Hand the drive back to polled code. Succeeds only when it is idle and nothing is queued or wanted; cancel_all and wait for is_idle first.

Source

pub fn lease_state(&self) -> LeaseState

Where an audio lease stands.

Source

pub fn request_audio_lease(&mut self) -> LeaseState

Ask for the drive on behalf of CD-DA or XA playback.

New requests are still accepted but queue. A request in flight is aborted at its next sector (it ends Outcome::Cancelled with what landed; resume it with Request::remaining_after after the lease). When the drive has stopped the lease is granted: the CD interrupt source is masked, and the audio code may program the controller directly. Poll lease_state until it reads LeaseState::Granted if this returns Pending.

Before attach the program already holds the drive and this returns Granted without changing anything.

Source

pub fn try_audio_lease(&mut self) -> bool

Take the drive for audio only if it can be had right now, without waiting and without leaving anything behind.

Returns true when the lease is granted (the drive was idle, or the program still holds it before attach). Returns false when a request is in flight: nothing changes, no lease is left pending and the request is not aborted, so a read submitted afterwards cannot end up queued behind a lease that is granted later. Ask again when the drive is idle.

request_audio_lease is for audio that must displace a read; this is for audio that only wants the drive while nobody else is using it.

Source

pub fn withdraw_audio_lease(&mut self) -> bool

Withdraw a lease that was asked for and not yet granted. Returns whether one was pending.

Call it before submitting a read that must not wait for audio: a pending lease is granted the moment the drive stops, which parks every read queued behind it until the audio code gives the drive back. The request that was aborted for the lease still ends Outcome::Cancelled (resume it with Request::remaining_after). A lease that is already granted is ended with release_audio_lease.

Source

pub fn release_audio_lease(&mut self) -> bool

Give the drive back after audio playback, or withdraw a lease that is still pending. Returns whether there was a lease to end.

The audio code must have ended playback with Pause (never Stop: the motor then spins down, reports status 0 for a second or two, and reads started in that window failed on a console). The next transfer starts with a recovery Pause if Config::pause_after_audio is set.

Source

pub fn submit(&mut self, request: Request) -> Result<Ticket, SubmitError>

Queue a request. It starts at once when the drive is free.

Higher priority (lower number) goes first; equal priorities go in submission order, except that one starting at the sector after the active request’s last is picked ahead of its peers so it chains without a seek.

Source

pub fn cancel(&mut self, ticket: Ticket) -> bool

Stop a request. Returns whether ticket was queued or active.

A queued request is dropped at once (Outcome::Cancelled, 0 sectors). The active one is flagged: the handler drops the next sector, pauses the drive, and the request ends Cancelled with the sectors that had landed.

Source

pub fn cancel_all(&mut self)

cancel everything: the queue and the active request.

Source

pub fn state(&mut self, ticket: Ticket) -> RequestState

Where ticket is. Also runs the no-progress check (service).

Source

pub fn service(&mut self)

Foreground upkeep: fail a transfer that has made no progress for Config::timeout_vblanks. state calls this; a main loop that never asks about a ticket should call it once per frame.

Source

pub fn on_interrupt(&mut self)

Handle one CD interrupt. Called by the interrupt handler (the crate’s exception wrapper on the console, the test harness on the host).

Source

pub fn source_wanted(&self) -> bool

Whether the CD interrupt source should reach the CPU right now.

Source

pub fn close_source(&mut self)

Close the CPU-side CD source. The console wrapper calls this before it touches the engine from the foreground, so the handler cannot run meanwhile; every foreground method ends by opening it again if source_wanted.

Source

pub fn note_source_closed(&mut self)

Record that the source was closed behind the engine’s back (by the console wrapper, through I_MASK directly), so the next sync_source reopens it.

Source

pub fn sync_source(&mut self)

Open or close the source to match source_wanted.

Auto Trait Implementations§

§

impl<H> Freeze for Engine<H>
where H: Freeze,

§

impl<H> RefUnwindSafe for Engine<H>
where H: RefUnwindSafe,

§

impl<H> !Send for Engine<H>

§

impl<H> !Sync for Engine<H>

§

impl<H> Unpin for Engine<H>
where H: Unpin,

§

impl<H> UnsafeUnpin for Engine<H>
where H: UnsafeUnpin,

§

impl<H> UnwindSafe for Engine<H>
where H: UnwindSafe,

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.