spectrusty_peripherals/bus/
joystick.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! A bus device for connecting joysticks.
9use core::str::FromStr;
10use core::convert::TryFrom;
11use core::num::NonZeroU16;
12use core::fmt;
13use core::marker::PhantomData;
14use core::ops::{Deref, DerefMut};
15use std::borrow::Cow;
16
17#[cfg(feature = "snapshot")]
18use serde::{Serialize, Deserialize};
19
20use spectrusty_core::{
21    bus::{BusDevice, PortAddress}
22};
23
24use super::ay::PassByAyAudioBusDevice;
25
26pub use crate::joystick::{
27    JoystickDevice, JoystickInterface, NullJoystickDevice,
28    kempston::*, fuller::*, sinclair::*, cursor::*
29};
30
31/// A convenient Kempston Joystick [BusDevice] type.
32pub type KempstonJoystick<D> = JoystickBusDevice<
33                                                KempstonJoyPortAddress,
34                                                KempstonJoystickDevice,
35                                                D>;
36/// A convenient Fuller Joystick [BusDevice] type.
37pub type FullerJoystick<D> = JoystickBusDevice<
38                                                FullerJoyPortAddress,
39                                                FullerJoystickDevice,
40                                                D>;
41/// A convenient pair of Left and Right Joystick [BusDevice] type.
42pub type SinclairJoystick<D> = SinclairLeftJoystick<SinclairRightJoystick<D>>;
43/// A convenient Right (Player 1) Sinclair Joystick [BusDevice] type.
44pub type SinclairRightJoystick<D> = JoystickBusDevice<
45                                                    SinclairRightJoyPortAddress,
46                                                    SinclairJoystickDevice<SinclairJoyRightMap>,
47                                                    D>;
48/// A convenient Left (Player 2) Sinclair Joystick [BusDevice] type.
49pub type SinclairLeftJoystick<D> = JoystickBusDevice<
50                                                    SinclairLeftJoyPortAddress,
51                                                    SinclairJoystickDevice<SinclairJoyLeftMap>,
52                                                    D>;
53/// A convenient Cursor Joystick [BusDevice] type.
54pub type CursorJoystick<D> = JoystickBusDevice<
55                                                CursorJoyPortAddress,
56                                                CursorJoystickDevice,
57                                                D>;
58macro_rules! joystick_names {
59    ($($ty:ty: $name:expr),*) => { $(
60        impl<D> fmt::Display for $ty {
61            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62                f.write_str($name)
63            }
64        }
65    )*};
66}
67
68joystick_names! {
69    KempstonJoystick<D>: "Kempston Joystick",
70    FullerJoystick<D>: "Fuller Joystick",
71    SinclairRightJoystick<D>: "Sinclair #1 Joystick",
72    SinclairLeftJoystick<D>: "Sinclair #2 Joystick",
73    CursorJoystick<D>: "Cursor Joystick"
74}
75
76/// A joystick controller, providing a [BusDevice] implementation that can be used with [joystick devices][JoystickDevice].
77#[derive(Clone, Default, Debug)]
78#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
79pub struct JoystickBusDevice<P, J, D>
80{
81    /// A [JoystickDevice] implementation, which may also implement [JoystickInterface] trait
82    /// for providing user input.
83    #[cfg_attr(feature = "snapshot", serde(skip))]
84    pub joystick: J,
85    #[cfg_attr(feature = "snapshot", serde(default))]
86    bus: D,
87    #[cfg_attr(feature = "snapshot", serde(skip))]
88    _port_decode: PhantomData<P>,
89}
90
91/// Kempston Joystick [PortAddress].
92#[derive(Clone, Copy, Default, Debug)]
93pub struct KempstonJoyPortAddress;
94impl PortAddress for KempstonJoyPortAddress {
95    const ADDRESS_MASK: u16 = 0x0020;
96    const ADDRESS_BITS: u16 = 0x001f;
97}
98/// Fuller Joystick [PortAddress].
99#[derive(Clone, Copy, Default, Debug)]
100pub struct FullerJoyPortAddress;
101impl PortAddress for FullerJoyPortAddress {
102    const ADDRESS_MASK: u16 = 0x00ff;
103    const ADDRESS_BITS: u16 = 0x007f;
104}
105/// Left Sinclair Joystick [PortAddress].
106#[derive(Clone, Copy, Default, Debug)]
107pub struct SinclairLeftJoyPortAddress;
108impl PortAddress for SinclairLeftJoyPortAddress {
109    const ADDRESS_MASK: u16 = 0x0800;
110    const ADDRESS_BITS: u16 = 0xf7fe;
111}
112/// Right Sinclair Joystick [PortAddress].
113#[derive(Clone, Copy, Default, Debug)]
114pub struct SinclairRightJoyPortAddress;
115impl PortAddress for SinclairRightJoyPortAddress {
116    const ADDRESS_MASK: u16 = 0x1000;
117    const ADDRESS_BITS: u16 = 0xeffe;
118}
119/// Cursor Joystick [PortAddress].
120#[derive(Clone, Copy, Default, Debug)]
121pub struct CursorJoyPortAddress;
122impl PortAddress for CursorJoyPortAddress {
123    const ADDRESS_MASK: u16 = 0x1800;
124    const ADDRESS_BITS: u16 = 0xe7fe;
125    /// Matches addresses: `0xeffe` or `0xf7fe` or `0xe7fe`.
126    #[inline]
127    fn match_port(address: u16) -> bool {
128        address & Self::ADDRESS_MASK != Self::ADDRESS_MASK
129    }
130}
131
132impl<P, J: JoystickInterface, D> Deref for JoystickBusDevice<P, J, D> {
133    type Target = J;
134    fn deref(&self) -> &Self::Target {
135        &self.joystick
136    }
137}
138
139impl<P, J: JoystickInterface, D> DerefMut for JoystickBusDevice<P, J, D> {
140    fn deref_mut(&mut self) -> &mut Self::Target {
141        &mut self.joystick
142    }
143}
144
145impl<P, J, D> PassByAyAudioBusDevice for JoystickBusDevice<P, J, D> {}
146
147impl<P, J, D> BusDevice for JoystickBusDevice<P, J, D>
148    where P: PortAddress,
149          D: BusDevice,
150          J: JoystickDevice
151{
152    type Timestamp = D::Timestamp;
153    type NextDevice = D;
154
155    #[inline]
156    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
157        &mut self.bus
158    }
159
160    #[inline]
161    fn next_device_ref(&self) -> &Self::NextDevice {
162        &self.bus
163    }
164
165    #[inline]
166    fn into_next_device(self) -> Self::NextDevice {
167        self.bus
168    }
169
170    #[inline]
171    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
172        let bus_data = self.bus.read_io(port, timestamp);
173        if P::match_port(port) {
174            let joy_data = self.joystick.port_read(port);
175            if let Some((data, ws)) = bus_data {
176                return Some((data & joy_data, ws))
177            }
178            return Some((joy_data, None))
179        }
180        bus_data
181    }
182
183    #[inline]
184    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
185        if P::match_port(port) && self.joystick.port_write(port, data) {
186            return Some(0);
187        }
188        self.bus.write_io(port, data, timestamp)
189    }
190}
191
192/// A selectable joystick controller, providing a [BusDevice] implementation.
193///
194/// This controller allows changing the implementation of the joystick device at run time.
195#[derive(Clone, Copy, Default, Debug)]
196#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
197pub struct MultiJoystickBusDevice<D> {
198    #[cfg_attr(feature = "snapshot", serde(default))]
199    pub joystick: JoystickSelect,
200    #[cfg_attr(feature = "snapshot", serde(default))]
201    bus: D
202}
203
204impl<D: Default> MultiJoystickBusDevice<D> {
205    pub fn new_with(joystick: JoystickSelect) -> Self {
206        MultiJoystickBusDevice {
207            joystick, bus: Default::default()
208        }
209    }
210}
211
212impl<D> Deref for MultiJoystickBusDevice<D> {
213    type Target = JoystickSelect;
214    fn deref(&self) -> &Self::Target {
215        &self.joystick
216    }
217}
218
219impl<D> DerefMut for MultiJoystickBusDevice<D> {
220    fn deref_mut(&mut self) -> &mut Self::Target {
221        &mut self.joystick
222    }
223}
224
225/// An enum of joystick device implementation variants.
226///
227/// Some of the variants contain more than one joystick device.
228#[derive(Clone, Copy, Debug)]
229#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
230#[cfg_attr(feature = "snapshot", serde(try_from = "Cow<str>", into = "&str"))]
231pub enum JoystickSelect {
232    Kempston(KempstonJoystickDevice),
233    Fuller(FullerJoystickDevice),
234    Sinclair(SinclairJoystickDevice<SinclairJoyRightMap>,
235             SinclairJoystickDevice<SinclairJoyLeftMap>),
236    Cursor(CursorJoystickDevice),
237}
238
239impl Default for JoystickSelect {
240    fn default() -> Self {
241        JoystickSelect::Kempston(Default::default())
242    }
243}
244
245impl fmt::Display for JoystickSelect {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        <&str>::from(self).fmt(f)
248    }
249}
250
251impl From<&JoystickSelect> for &str {
252   fn from(joy: &JoystickSelect) -> Self {
253        <&str>::from(*joy)
254   }
255 }
256
257impl From<JoystickSelect> for &str {
258    fn from(joy: JoystickSelect) -> Self {
259        use JoystickSelect::*;
260        match joy {
261            Kempston(..) => "Kempston",
262            Fuller(..)   => "Fuller",
263            Sinclair(..) => "Sinclair",
264            Cursor(..)   => "Cursor",
265        }
266    }
267}
268
269/// An error that can be returned when parsing a [JoystickSelect] variant.
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub struct ParseJoystickSelectError;
272
273impl fmt::Display for ParseJoystickSelectError {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        "unrecognized joystick name".fmt(f)
276    }
277}
278
279impl std::error::Error for ParseJoystickSelectError {}
280
281impl FromStr for JoystickSelect {
282    type Err = ParseJoystickSelectError;
283
284    fn from_str(name: &str) -> Result<Self, Self::Err> {
285        match JoystickSelect::new_from_name(name) {
286            Some((joy, _)) => Ok(joy),
287            None => Err(ParseJoystickSelectError)
288        }
289    }
290}
291
292/// The error type returned when a [JoystickSelect] variant conversion failed.
293#[derive(Debug, Clone, PartialEq, Eq)]
294pub struct TryFromStrJoystickSelectError<'a>(pub Cow<'a, str>);
295
296impl fmt::Display for TryFromStrJoystickSelectError<'_> {
297    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298        write!(f, "unrecognized joystick name: {}", self.0)
299    }
300}
301
302impl std::error::Error for TryFromStrJoystickSelectError<'_> {}
303
304impl<'a> TryFrom<&'a str> for JoystickSelect {
305    type Error = TryFromStrJoystickSelectError<'a>;
306    fn try_from(name: &'a str) -> Result<Self, Self::Error> {
307        match JoystickSelect::new_from_name(name) {
308            Some((joy, _)) => Ok(joy),
309            None => Err(TryFromStrJoystickSelectError(name.into()))
310        }
311    }
312}
313
314impl<'a> TryFrom<Cow<'a, str>> for JoystickSelect {
315    type Error = TryFromStrJoystickSelectError<'a>;
316    fn try_from(name: Cow<'a, str>) -> Result<Self, Self::Error> {
317        match JoystickSelect::new_from_name(&name) {
318            Some((joy, _)) => Ok(joy),
319            None => Err(TryFromStrJoystickSelectError(name))
320        }
321    }
322}
323
324#[allow(clippy::len_without_is_empty)]
325impl JoystickSelect {
326    /// The largest value that can be passed as a `global_index` to [JoystickSelect::new_with_index].
327    pub const MAX_GLOBAL_INDEX: usize = 4;
328    /// Creates a new joystick device variant from a given name.
329    ///
330    /// On success returns a tuple with one of the joystick variants and the number of
331    /// available joysticks in this variant.
332    pub fn new_from_name<S: AsRef<str>>(name: S) -> Option<(Self, usize)> {
333        let name = name.as_ref();
334        use JoystickSelect::*;
335        if name.eq_ignore_ascii_case("Kempston") {
336            Some((Kempston(Default::default()), 1))
337        }
338        else if name.eq_ignore_ascii_case("Fuller") {
339            Some((Fuller(Default::default()), 1))
340        }
341        else if name.eq_ignore_ascii_case("Cursor")
342              ||name.eq_ignore_ascii_case("Protek")
343              ||name.eq_ignore_ascii_case("AGF") {
344            Some((Cursor(Default::default()), 1))
345        }
346        else if name.eq_ignore_ascii_case("Sinclair")
347             ||name.eq_ignore_ascii_case("Interface II")
348             ||name.eq_ignore_ascii_case("Interface 2")
349             ||name.eq_ignore_ascii_case("IF II")
350             ||name.eq_ignore_ascii_case("IF 2") {
351            Some((Sinclair(Default::default(), Default::default()), 2))
352        }
353        else {
354            None
355        }
356    }
357    /// Creates a new joystick device variant from a given `global_index` which counts for
358    /// all joysticks in every variant.
359    ///
360    /// E.g. `2` and `3` both select `Sinclair` joystick but with a different resulting index.
361    ///
362    /// On success returns a tuple with one of the joystick variants and an index of
363    /// the selected joystick in this variant.
364    ///
365    /// See also [JoystickSelect::MAX_GLOBAL_INDEX].
366    pub fn new_with_index(global_index: usize) -> Option<(Self, usize)> {
367        use JoystickSelect::*;
368        match global_index {
369            0 => Some((Kempston(Default::default()), 0)),
370            1 => Some((Fuller(Default::default()), 0)),
371            i@2|i@3 => Some((Sinclair(Default::default(), Default::default()), i-2)),
372            4 => Some((Cursor(Default::default()), 0)),
373            _ => None
374        }
375    }
376    /// Returns the number of joysticks in the current variant.
377    pub fn len(&self) -> usize {
378        if let JoystickSelect::Sinclair(..) = self {
379            return 2
380        }
381        1
382    }
383    /// Provides a mutable reference to the selected joystick type via a dynamic trait.
384    ///
385    /// Some variants contain more than one joystick, so provide an appropriate `index` to
386    /// select one of them.
387    ///
388    /// Returns `None` if a joystick with the given index doesn't exist.
389    pub fn joystick_interface(&mut self, index: usize) -> Option<&mut (dyn JoystickInterface + 'static)>
390    {
391        match self {
392            JoystickSelect::Kempston(ref mut joy) if index == 0 => Some(joy),
393            JoystickSelect::Fuller(ref mut joy) if index == 0 => Some(joy),
394            JoystickSelect::Sinclair(ref mut joy, _) if index == 0 => Some(joy),
395            JoystickSelect::Sinclair(_, ref mut joy) if index == 1 => Some(joy),
396            JoystickSelect::Cursor(ref mut joy) if index == 0 => Some(joy),
397            _ => None
398        }
399    }
400    /// Returns an `index` of the next joystick in the current variant.
401    /// Optionally changes the current joystick device variant to the next one cyclically.
402    ///
403    /// Some variants contain more than one joystick, provide an appropriate `index` to
404    /// select one of them. If `index + 1` exceeds the number of joysticks available then
405    /// the next variant is being selected.
406    pub fn select_next_joystick(&mut self, index: usize) -> usize {
407        use JoystickSelect::*;
408        *self = match self {
409            Kempston(..) => Fuller(Default::default()),
410            Fuller(..) => Sinclair(Default::default(), Default::default()),
411            Sinclair(..) if index == 0 => return 1,
412            Sinclair(..) => Cursor(Default::default()),
413            Cursor(..) => Kempston(Default::default()),
414        };
415        0
416    }
417    #[inline]
418    pub fn is_last(&self) -> bool {
419        self.is_cursor()
420    }
421    #[inline]
422    pub fn is_kempston(&self) -> bool {
423        if let JoystickSelect::Kempston(..) = self {
424            return true
425        }
426        false
427    }
428    #[inline]
429    pub fn is_fuller(&self) -> bool {
430        if let JoystickSelect::Fuller(..) = self {
431            return true
432        }
433        false
434    }
435    #[inline]
436    pub fn is_sinclair(&self) -> bool {
437        if let JoystickSelect::Sinclair(..) = self {
438            return true
439        }
440        false
441    }
442    #[inline]
443    pub fn is_cursor(&self) -> bool {
444        if let JoystickSelect::Cursor(..) = self {
445            return true
446        }
447        false
448    }
449}
450
451impl<D> PassByAyAudioBusDevice for MultiJoystickBusDevice<D> {}
452
453impl<D> BusDevice for MultiJoystickBusDevice<D>
454    where D: BusDevice
455{
456    type Timestamp = D::Timestamp;
457    type NextDevice = D;
458
459    #[inline(always)]
460    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
461        &mut self.bus
462    }
463
464    #[inline(always)]
465    fn next_device_ref(&self) -> &Self::NextDevice {
466        &self.bus
467    }
468
469    #[inline]
470    fn into_next_device(self) -> Self::NextDevice {
471        self.bus
472    }
473
474    #[inline(always)]
475    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
476        use JoystickSelect::*;
477        let bus_data = self.bus.read_io(port, timestamp);
478        let joy_data = match self.joystick {
479            Kempston(joystick) if KempstonJoyPortAddress::match_port(port) => {
480                Some(joystick.port_read(port))
481            }
482            Fuller(joystick) if FullerJoyPortAddress::match_port(port) => {
483                Some(joystick.port_read(port))
484            }
485            Sinclair(joy1, joy2) => {
486                let joy_data1 = if SinclairRightJoyPortAddress::match_port(port) {
487                    Some(joy1.port_read(port))
488                }
489                else {
490                    None
491                };
492                let joy_data2 = if SinclairLeftJoyPortAddress::match_port(port) {
493                    Some(joy2.port_read(port))
494                }
495                else {
496                    None
497                };
498                match (joy_data1, joy_data2) {
499                    (Some(data1), Some(data2)) => Some(data1 & data2),
500                    (Some(data1), None) => Some(data1),
501                    (None, Some(data2)) => Some(data2),
502                    _ => None
503                }
504            }
505            Cursor(joystick) if CursorJoyPortAddress::match_port(port) => {
506                Some(joystick.port_read(port))
507            }
508            _ => None
509        };
510        if let Some(joy_data) = joy_data {
511            if let Some((data, ws)) = bus_data {
512                return Some((data & joy_data, ws))
513            }
514            Some((joy_data, None))
515        }
516        else {
517            bus_data
518        }
519    }
520}
521
522#[cfg(test)]
523#[cfg(feature = "snapshot")]
524mod tests {
525    use super::*;
526
527    #[test]
528    fn joystick_select_snapshot() {
529        let (joy, len) = JoystickSelect::new_from_name("Sinclair").unwrap();
530        assert_eq!(len, 2);
531        assert!(joy.is_sinclair());
532        assert_eq!(joy.len(), len);
533        assert_eq!(<&str>::from(joy), "Sinclair");
534        let json = r#""Sinclair""#;
535        assert_eq!(serde_json::to_string(&joy).unwrap(), json);
536        let joy0: JoystickSelect = serde_json::from_str(json).unwrap();
537        let joy1: JoystickSelect = serde_json::from_reader(json.as_bytes()).unwrap();
538        assert!(joy0.is_sinclair());
539        assert!(joy1.is_sinclair());
540        assert_eq!(format!("{}", joy0), "Sinclair");
541    }
542}