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

psx_io/
controller_port.rs

1//! The controller and memory-card port: one serial transport shared by every
2//! driver on it.
3//!
4//! A pad and a memory card sit on the same wires, so a program has one owner
5//! for them, the [`ControllerPort`] token, and the drivers (`psx-pad`,
6//! `psx-mc`) borrow it for the length of a transaction. What they borrow is
7//! the [`Transport`]: select a socket, clock bytes with the device's `/ACK`
8//! pulse pacing each one, release the socket. The pad's timing is the
9//! reference, since it was tuned on an original SCPH-1200, a clone and an
10//! SCPH-110; a memory card uses the same exchange with a longer budget for the
11//! `/ACK` after a flash write.
12//!
13//! Register addresses and bit layouts live in [`psx_hw::sio::sio0`].
14//!
15//! ```text
16//! let mut port = peripherals.controller_port;
17//! let mut pads = psx_pad::PadReader::port1();
18//! let state = pads.poll(&mut port);
19//! let mut card = psx_mc::Card::new(psx_mc::HardwareCard::on_port(&mut port, Slot::One));
20//! ```
21
22use crate::periph::ControllerPort;
23use crate::{read_u32, read_u8, write_u16, write_u8};
24use psx_hw::sio::sio0;
25
26/// Which socket a transaction addresses.
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28pub enum Port {
29    /// Controller port 1 and memory-card slot 1.
30    One,
31    /// Controller port 2 and memory-card slot 2.
32    Two,
33}
34
35impl Port {
36    /// Whether this is the second socket.
37    #[inline(always)]
38    pub const fn is_two(self) -> bool {
39        matches!(self, Port::Two)
40    }
41}
42
43/// Spin budgets for one transaction. Each spin is one bounded `STAT` read, so
44/// the budgets bound the time a missing or wedged device can cost.
45#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub struct Timing {
47    /// Delay after selecting the socket, before the first byte. The original
48    /// SCPH-1200 gives no answer without it; fast clones do not care.
49    pub setup_spins: u32,
50    /// Bound on the byte shift itself (TX ready, then RX filled).
51    pub byte_spins: u32,
52    /// Bound on each wait for the device's `/ACK` to assert, and again to
53    /// release.
54    pub ack_spins: u32,
55}
56
57impl Timing {
58    /// The pad's timing, measured on silicon: 1,024 setup spins (the
59    /// SCPH-1200 floor sits between 384 and 768), and a 2,048-spin `/ACK`
60    /// wait, which exceeds the BIOS's own 100 microsecond DSR timeout.
61    pub const PAD: Timing = Timing {
62        setup_spins: 1_024,
63        byte_spins: 32_768,
64        ack_spins: 2_048,
65    };
66
67    /// The memory card's timing: the pad's setup and byte budgets, and a long
68    /// `/ACK` budget because a card can hold `/ACK` back while a frame's flash
69    /// write commits.
70    pub const CARD: Timing = Timing {
71        setup_spins: 1_024,
72        byte_spins: 32_768,
73        ack_spins: 200_000,
74    };
75}
76
77/// Why one byte exchange failed.
78#[derive(Copy, Clone, Debug, PartialEq, Eq)]
79pub enum ExchangeError {
80    /// The port never became ready to take the byte.
81    TxTimeout,
82    /// The byte shifted out but no reply came back.
83    RxTimeout,
84    /// The reply came back and the device never pulsed `/ACK`; the reply is
85    /// kept because an absent device answers `0xFF` here.
86    AckTimeout {
87        /// The byte received before the wait.
88        reply: u8,
89    },
90    /// The device pulsed `/ACK` but held it asserted.
91    AckStuck {
92        /// The byte received before the wait.
93        reply: u8,
94    },
95}
96
97/// The six accesses the transport makes to the port, and the transaction
98/// steps built on them.
99///
100/// [`ControllerPort`] implements it on the registers. Host tests implement it
101/// on a model of a pad or a card, so the production timing code runs under
102/// test unchanged.
103pub trait Transport {
104    /// Read the status register.
105    fn status(&mut self) -> u32;
106    /// Pop one received byte.
107    fn receive(&mut self) -> u8;
108    /// Start shifting one byte out.
109    fn transmit(&mut self, byte: u8);
110    /// Write the mode register.
111    fn set_mode(&mut self, value: u16);
112    /// Write the baud-rate reload.
113    fn set_baud(&mut self, value: u16);
114    /// Write the control register.
115    fn set_control(&mut self, value: u16);
116
117    /// Configure the port for 250 kHz, 8N1 and assert `/CS` on `port`.
118    ///
119    /// The `/ACK` interrupt is left disarmed: arming it disturbed real
120    /// transfers (the SCPH-1200 stopped answering, a clone's bytes corrupted),
121    /// so the transport watches the live `/ACK` level in `STAT` instead.
122    fn select(&mut self, port: Port) {
123        self.select_with(port, false);
124    }
125
126    /// [`select`](Self::select) with the `/ACK` interrupt armed when
127    /// `arm_ack_irq` is set, so `STAT` latches each pulse. Only a diagnostic
128    /// pacing wants that.
129    fn select_with(&mut self, port: Port, arm_ack_irq: bool) {
130        self.set_mode(sio0::MODE_8N1);
131        self.set_baud(sio0::BAUD_250KHZ);
132        // Clear any stale IRQ latch from the previous transaction, then
133        // assert the select line.
134        self.set_control(sio0::ctrl::ACK);
135        self.set_control(sio0::selected_ctrl(port.is_two(), arm_ack_irq));
136    }
137
138    /// Release `/CS`, which resets the attached device's state machine.
139    fn deselect(&mut self) {
140        self.set_control(0);
141    }
142
143    /// Reset the port's UART. A deselect alone need not cancel a late reply
144    /// still in the shifter, so a failed transaction ends with this.
145    fn reset(&mut self) {
146        self.set_control(sio0::ctrl::RESET);
147    }
148
149    /// Pop up to a few stale bytes so the next reply lines up with the
150    /// first byte of the transaction. Bounded, so a stuck "RX not empty" flag
151    /// cannot spin forever.
152    fn drain_receive(&mut self) {
153        let mut popped = 0;
154        while self.status() & sio0::stat::RX_NOT_EMPTY != 0 && popped < 16 {
155            let _ = self.receive();
156            popped += 1;
157        }
158    }
159
160    /// Burn time with `spins` status reads, which the compiler cannot remove.
161    fn delay(&mut self, spins: u32) {
162        let mut remaining = spins;
163        while remaining > 0 {
164            let _ = self.status();
165            remaining -= 1;
166            core::hint::spin_loop();
167        }
168    }
169
170    /// Spin until any bit of `mask` is set in the status register, at most
171    /// `spins` reads after the first. `false` on timeout.
172    fn wait_status_set(&mut self, mask: u32, spins: u32) -> bool {
173        let mut remaining = spins;
174        while self.status() & mask == 0 {
175            if remaining == 0 {
176                return false;
177            }
178            remaining -= 1;
179            core::hint::spin_loop();
180        }
181        true
182    }
183
184    /// Spin until every bit of `mask` is clear in the status register, at most
185    /// `spins` reads after the first. `false` on timeout.
186    fn wait_status_clear(&mut self, mask: u32, spins: u32) -> bool {
187        let mut remaining = spins;
188        while self.status() & mask != 0 {
189            if remaining == 0 {
190                return false;
191            }
192            remaining -= 1;
193            core::hint::spin_loop();
194        }
195        true
196    }
197
198    /// Start a transaction: select `port`, wait out the setup time, drop
199    /// stale bytes, and wait for an `/ACK` left asserted by an aborted
200    /// transaction to release, so it is not counted as the first byte's.
201    ///
202    /// `false` means the line never released. Call [`finish`](Self::finish)
203    /// either way.
204    fn begin(&mut self, port: Port, timing: Timing) -> bool {
205        self.select(port);
206        self.delay(timing.setup_spins);
207        self.drain_receive();
208        self.wait_status_clear(sio0::stat::DSR_LEVEL, timing.ack_spins)
209    }
210
211    /// End a transaction: release `/CS`, and after a failed one reset the
212    /// UART as well. The next [`select`](Self::select) restores mode and baud.
213    fn finish(&mut self, succeeded: bool) {
214        self.deselect();
215        if !succeeded {
216            self.reset();
217        }
218    }
219
220    /// Clock one byte and wait for the device's `/ACK`.
221    ///
222    /// Waits for TX ready, writes `byte`, and watches the live `/ACK` level
223    /// while the reply shifts in, so a pulse that ends before the reply
224    /// arrives is not missed. Unless `is_last` (the final byte of a packet is
225    /// never acknowledged), it then waits for `/ACK` to assert if it has not
226    /// yet, and to release, before the next byte may go. RX-ready alone only
227    /// says the byte arrived, not that the device is ready for another.
228    ///
229    /// No control-register write happens mid-transaction.
230    fn exchange(&mut self, byte: u8, is_last: bool, timing: Timing) -> Result<u8, ExchangeError> {
231        if !self.wait_status_set(sio0::stat::TX_READY, timing.byte_spins) {
232            return Err(ExchangeError::TxTimeout);
233        }
234        self.transmit(byte);
235        let mut ack_seen = false;
236        let mut remaining = timing.byte_spins;
237        loop {
238            let status = self.status();
239            ack_seen |= status & sio0::stat::DSR_LEVEL != 0;
240            if status & sio0::stat::RX_NOT_EMPTY != 0 {
241                break;
242            }
243            if remaining == 0 {
244                return Err(ExchangeError::RxTimeout);
245            }
246            remaining -= 1;
247            core::hint::spin_loop();
248        }
249        let reply = self.receive();
250        if !is_last {
251            if !ack_seen && !self.wait_status_set(sio0::stat::DSR_LEVEL, timing.ack_spins) {
252                return Err(ExchangeError::AckTimeout { reply });
253            }
254            if !self.wait_status_clear(sio0::stat::DSR_LEVEL, timing.ack_spins) {
255                return Err(ExchangeError::AckStuck { reply });
256            }
257        }
258        Ok(reply)
259    }
260}
261
262impl Transport for ControllerPort {
263    #[inline(always)]
264    fn status(&mut self) -> u32 {
265        // SAFETY: STAT is the port's aligned 32-bit status register; reading it
266        // has no side effects. The `&mut` borrow of the token is the proof no
267        // other driver is mid-transaction.
268        unsafe { read_u32(sio0::STAT) }
269    }
270
271    #[inline(always)]
272    fn receive(&mut self) -> u8 {
273        // SAFETY: a byte read of DATA pops one RX FIFO byte.
274        unsafe { read_u8(sio0::DATA) }
275    }
276
277    #[inline(always)]
278    fn transmit(&mut self, byte: u8) {
279        // SAFETY: a byte write to DATA clocks it out of the port.
280        unsafe { write_u8(sio0::DATA, byte) }
281    }
282
283    #[inline(always)]
284    fn set_mode(&mut self, value: u16) {
285        // SAFETY: MODE is the port's 16-bit mode register.
286        unsafe { write_u16(sio0::MODE, value) }
287    }
288
289    #[inline(always)]
290    fn set_baud(&mut self, value: u16) {
291        // SAFETY: BAUD is the port's 16-bit baud-rate reload.
292        unsafe { write_u16(sio0::BAUD, value) }
293    }
294
295    #[inline(always)]
296    fn set_control(&mut self, value: u16) {
297        // SAFETY: CTRL is the port's 16-bit control register; it selects the
298        // socket and resets the port, nothing else.
299        unsafe { write_u16(sio0::CTRL, value) }
300    }
301}
302
303#[cfg(test)]
304mod tests {
305    extern crate std;
306
307    use super::*;
308    use std::vec::Vec;
309
310    /// A scripted port: each status read pops the next scripted word, and
311    /// every write is logged.
312    #[derive(Default)]
313    struct Script {
314        status: Vec<u32>,
315        reads: usize,
316        received: Vec<u8>,
317        sent: Vec<u8>,
318        control: Vec<u16>,
319    }
320
321    impl Script {
322        fn with_status(words: &[u32]) -> Self {
323            Script {
324                status: words.to_vec(),
325                ..Script::default()
326            }
327        }
328    }
329
330    impl Transport for Script {
331        fn status(&mut self) -> u32 {
332            // Past the script, hold the last word.
333            let word = self
334                .status
335                .get(self.reads)
336                .or(self.status.last())
337                .copied()
338                .unwrap_or(0);
339            self.reads += 1;
340            word
341        }
342        fn receive(&mut self) -> u8 {
343            self.received.pop().unwrap_or(0xFF)
344        }
345        fn transmit(&mut self, byte: u8) {
346            self.sent.push(byte);
347        }
348        fn set_mode(&mut self, _value: u16) {}
349        fn set_baud(&mut self, _value: u16) {}
350        fn set_control(&mut self, value: u16) {
351            self.control.push(value);
352        }
353    }
354
355    const TX: u32 = sio0::stat::TX_READY;
356    const RX: u32 = sio0::stat::RX_NOT_EMPTY;
357    const DSR: u32 = sio0::stat::DSR_LEVEL;
358    const TIMING: Timing = Timing {
359        setup_spins: 2,
360        byte_spins: 4,
361        ack_spins: 4,
362    };
363
364    #[test]
365    fn the_token_is_the_only_way_to_a_port_that_implements_the_transport() {
366        fn takes_transport<T: Transport>(_: &mut T) {}
367        // SAFETY: a test-local token on the host, where no register is touched.
368        let mut port = unsafe { ControllerPort::steal() };
369        takes_transport(&mut port);
370        assert_eq!(core::mem::size_of::<ControllerPort>(), 0);
371    }
372
373    #[test]
374    fn select_clears_the_latch_then_asserts_the_port_without_arming_the_ack_interrupt() {
375        for (port, selected) in [(Port::One, 0x0003), (Port::Two, 0x2003)] {
376            let mut bus = Script::default();
377            bus.select(port);
378            assert_eq!(bus.control, [sio0::ctrl::ACK, selected]);
379            assert_eq!(selected & sio0::ctrl::ACK_IRQ_EN, 0);
380        }
381    }
382
383    #[test]
384    fn a_failed_transaction_resets_the_uart_and_a_clean_one_only_releases_the_port() {
385        let mut bus = Script::default();
386        bus.finish(true);
387        assert_eq!(bus.control, [0]);
388        let mut bus = Script::default();
389        bus.finish(false);
390        assert_eq!(bus.control, [0, sio0::ctrl::RESET]);
391    }
392
393    #[test]
394    fn begin_drops_stale_bytes_and_waits_for_a_held_ack_to_release() {
395        // One stale byte (RX set), then drained, then DSR still high twice
396        // before it releases.
397        let mut bus = Script::with_status(&[0, 0, RX, 0, DSR, DSR, 0]);
398        bus.received.push(0x55);
399        assert!(bus.begin(Port::One, TIMING));
400        assert!(bus.received.is_empty());
401
402        let mut stuck = Script::with_status(&[DSR]);
403        assert!(!stuck.begin(Port::One, TIMING));
404    }
405
406    #[test]
407    fn an_acknowledged_byte_waits_for_ack_to_assert_and_release() {
408        // TX ready; RX arrives at once without an ack; ack asserts, releases.
409        let mut bus = Script::with_status(&[TX, RX, DSR, 0]);
410        bus.received.push(0x41);
411        assert_eq!(bus.exchange(0x42, false, TIMING), Ok(0x41));
412        assert_eq!(bus.sent, [0x42]);
413        // No control write happened inside the exchange.
414        assert!(bus.control.is_empty());
415    }
416
417    #[test]
418    fn an_ack_seen_while_the_reply_shifts_in_is_not_waited_for_again() {
419        // The pulse lands during the RX wait (DSR set, RX not yet), ends, and
420        // RX arrives after: only the release wait remains.
421        let mut bus = Script::with_status(&[TX, DSR, RX, 0]);
422        bus.received.push(0x5A);
423        assert_eq!(bus.exchange(0x00, false, TIMING), Ok(0x5A));
424    }
425
426    #[test]
427    fn the_last_byte_of_a_packet_is_never_waited_on() {
428        let mut bus = Script::with_status(&[TX, RX]);
429        bus.received.push(0x80);
430        assert_eq!(bus.exchange(0x00, true, TIMING), Ok(0x80));
431        // Two status reads: TX ready and RX ready, nothing after.
432        assert_eq!(bus.reads, 2);
433    }
434
435    #[test]
436    fn each_failure_is_reported_with_its_stage() {
437        let mut bus = Script::with_status(&[0]);
438        assert_eq!(
439            bus.exchange(0x01, false, TIMING),
440            Err(ExchangeError::TxTimeout)
441        );
442        assert!(bus.sent.is_empty());
443
444        let mut bus = Script::with_status(&[TX, 0]);
445        assert_eq!(
446            bus.exchange(0x01, false, TIMING),
447            Err(ExchangeError::RxTimeout)
448        );
449
450        let mut bus = Script::with_status(&[TX, RX, 0]);
451        bus.received.push(0xFF);
452        assert_eq!(
453            bus.exchange(0x01, false, TIMING),
454            Err(ExchangeError::AckTimeout { reply: 0xFF })
455        );
456
457        let mut bus = Script::with_status(&[TX, RX | DSR, DSR]);
458        bus.received.push(0x41);
459        assert_eq!(
460            bus.exchange(0x01, false, TIMING),
461            Err(ExchangeError::AckStuck { reply: 0x41 })
462        );
463    }
464
465    #[test]
466    fn the_pad_and_card_budgets_differ_only_in_the_ack_wait() {
467        assert_eq!(
468            Timing {
469                ack_spins: Timing::PAD.ack_spins,
470                ..Timing::CARD
471            },
472            Timing::PAD
473        );
474        const { assert!(Timing::CARD.ack_spins > Timing::PAD.ack_spins) };
475    }
476}