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}