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

psx_rt/
peripherals.rs

1//! The one-per-program set of peripheral ownership tokens.
2
3use crate::critical_section::{self, Mutex};
4use core::cell::Cell;
5use psx_io::periph::{Cd, ControllerPort, GpuDma, MdecDma, OrderingTableClearDma, SpuDma};
6
7/// Every shared-state peripheral token, handed out once.
8///
9/// ```ignore
10/// let p = psx_rt::Peripherals::take().unwrap();
11/// let mut gpu = psx_gpu::Gpu::new(p.gpu_dma, display);
12/// let mut port = p.controller_port; // pads and memory cards borrow it
13/// let mut spu = psx_spu::Spu::new(p.spu_dma);
14/// let cd = p.cd; // or `SectorReader::with_cd(cd)`, `xa::Player::new(cd)`
15/// ```
16///
17/// Each token is owned by the driver that programs its device; see
18/// `psx_io::periph` for who that is.
19///
20/// The tokens are zero-sized, so `Peripherals` is too; moving it or its
21/// fields around costs nothing.
22#[derive(Debug)]
23#[non_exhaustive]
24pub struct Peripherals {
25    /// DMA channel 2 and the GP0 stream it feeds.
26    pub gpu_dma: GpuDma,
27    /// DMA channel 6, the ordering-table clear.
28    pub ordering_table_clear_dma: OrderingTableClearDma,
29    /// DMA channels 0 and 1 (MDEC in and out).
30    pub mdec_dma: MdecDma,
31    /// DMA channel 4 and the SPU transfer port.
32    pub spu_dma: SpuDma,
33    /// The CD-ROM controller and DMA channel 3.
34    pub cd: Cd,
35    /// The pad and memory-card port (SIO0).
36    pub controller_port: ControllerPort,
37}
38
39static TAKEN: Mutex<Cell<bool>> = Mutex::new(Cell::new(false));
40
41impl Peripherals {
42    /// The token set, the first time this is called; `None` after that.
43    ///
44    /// One flag test inside a short critical section.
45    pub fn take() -> Option<Self> {
46        critical_section::with(|cs| {
47            let taken = TAKEN.borrow(cs);
48            if taken.get() {
49                return None;
50            }
51            taken.set(true);
52            // SAFETY: the flag guarantees this is the only set handed out by
53            // `take`; `steal` callers carry their own contract.
54            Some(unsafe { Self::steal() })
55        })
56    }
57
58    /// The token set, whether or not [`take`](Self::take) already ran.
59    ///
60    /// # Safety
61    ///
62    /// The caller must not use a token from this set while the same token
63    /// from another set is in use (see `psx_io::periph`).
64    pub unsafe fn steal() -> Self {
65        // SAFETY: forwarded contract.
66        unsafe {
67            Self {
68                gpu_dma: GpuDma::steal(),
69                ordering_table_clear_dma: OrderingTableClearDma::steal(),
70                mdec_dma: MdecDma::steal(),
71                spu_dma: SpuDma::steal(),
72                cd: Cd::steal(),
73                controller_port: ControllerPort::steal(),
74            }
75        }
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn take_hands_the_set_out_once() {
85        assert_eq!(core::mem::size_of::<Peripherals>(), 0);
86        let first = Peripherals::take();
87        assert!(first.is_some());
88        assert!(Peripherals::take().is_none());
89    }
90}