psx_cdstream/hw.rs
1//! The drive as the transport state machine sees it.
2//!
3//! [`CdHw`] is everything [`Engine`](crate::Engine) asks of the CD-ROM
4//! controller and the system around it. The real implementation
5//! (`Mmio`, target only) is a thin layer over `psx_io::periph::Cd`; the tests
6//! use a scripted fake drive. The methods are the controller steps whose
7//! *order* matters on silicon, grouped so that order lives in one place
8//! (the implementation) and the state machine only decides which step comes
9//! next.
10
11/// Controller steps and system services the state machine needs.
12///
13/// Nothing here blocks beyond a short bounded poll: every method is called
14/// from the CD interrupt handler or from a foreground section that has the
15/// CD source masked.
16pub trait CdHw {
17 /// Whether to record controller events through [`trace`](Self::trace).
18 /// Constant per implementation so the unused event words fold away.
19 const TRACE: bool = false;
20
21 /// Send one drive command and arm the controller to interrupt for its
22 /// responses. Returns `false` when the parameter FIFO never had room (the
23 /// command is not sent).
24 ///
25 /// The order is: mask the controller's interrupt output, acknowledge
26 /// every pending flag and the CPU-side pending bit, discard any response
27 /// bytes, empty the parameter FIFO, push the parameters, write the
28 /// command byte, then enable the controller's interrupt output. Silicon
29 /// is picky about it (the SDK's polled reader does the same).
30 fn issue(&mut self, command: u8, params: &[u8]) -> bool;
31
32 /// The controller's current interrupt flag (0 none, 1 data ready, 2
33 /// complete, 3 acknowledge, 5 error).
34 fn interrupt_code(&mut self) -> u8;
35
36 /// The first two response bytes of a drive error: status and error code.
37 fn error_response(&mut self) -> (u8, u8);
38
39 /// Pop and discard the response FIFO.
40 fn discard_response(&mut self);
41
42 /// Acknowledge interrupt flag `bits` on the controller and the CPU-side
43 /// pending bit. `acknowledge(0)` only clears the CPU-side bit.
44 fn acknowledge(&mut self, bits: u8);
45
46 /// Mask the controller's interrupt output, so nothing more is raised
47 /// until the next [`issue`](Self::issue).
48 fn silence_output(&mut self);
49
50 /// Drop a data request left armed by the previous transfer. Called
51 /// before the first command of a transfer that did not start from a
52 /// recovery pause.
53 fn drop_data_request(&mut self);
54
55 /// Pop one sector's 2048 bytes from the data FIFO as 512 little-endian
56 /// words, storing the first `store_words` of them at `destination`.
57 /// The rest are popped and dropped (the FIFO has to be emptied either
58 /// way). Arms the data request and waits a bounded time for the FIFO to
59 /// fill; `false` if it never does.
60 ///
61 /// # Safety
62 ///
63 /// `destination` must be valid for `store_words` word writes (it is not
64 /// used when `store_words` is 0).
65 unsafe fn pop_sector(&mut self, destination: *mut u32, store_words: usize) -> bool;
66
67 /// Let the CD interrupt source reach the CPU (or not).
68 ///
69 /// This only moves the mask. It must not acknowledge the CPU-side pending
70 /// bit: the foreground closes the source around every call, and an
71 /// interrupt that arrived meanwhile has to fire when the source opens.
72 /// Stale flags are cleared by [`acknowledge`](Self::acknowledge) and by
73 /// [`issue`](Self::issue).
74 fn set_source_enabled(&mut self, enabled: bool);
75
76 /// Monotonic VBlank count, the clock the no-progress timeout runs on.
77 fn vblank_count(&mut self) -> u32;
78
79 /// A free-running timestamp for measuring the handler, wrapping at 16
80 /// bits. Implementations that do not measure return a constant.
81 fn clock(&mut self) -> u16 {
82 0
83 }
84
85 /// Record one trace word (only called when [`TRACE`](Self::TRACE) is set).
86 fn trace(&mut self, _word: u32) {}
87}