spectrusty_core/
bus.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//! System bus device emulators to be used with [ControlUnit][crate::chip::ControlUnit]s.
9use core::num::NonZeroU16;
10use core::fmt::{self, Debug};
11use core::any::TypeId;
12use core::marker::PhantomData;
13use core::ops::{Deref, DerefMut};
14
15#[cfg(feature = "snapshot")]
16use serde::{Serialize, Deserialize};
17
18mod dynbus;
19
20use crate::clock::VFrameTs;
21
22pub use dynbus::*;
23
24impl<T: 'static> dyn NamedBusDevice<T> {
25    /// Attempts to downcast the box to a concrete type.
26    #[inline]
27    pub fn downcast<D: 'static>(self: Box<Self>) -> Result<Box<D>, Box<dyn NamedBusDevice<T>>>
28        where D: NamedBusDevice<T>
29    {
30        if self.is::<D>() {
31            unsafe {
32                let raw: *mut dyn NamedBusDevice<T> = Box::into_raw(self);
33                Ok(Box::from_raw(raw as *mut D))
34            }
35        } else {
36            Err(self)
37        }
38    }
39}
40
41impl<T: 'static> dyn NamedBusDevice<T> + 'static {
42    /// Returns `true` if the boxed type is the same as `D`
43    #[inline]
44    pub fn is<D: NamedBusDevice<T> + 'static>(&self) -> bool {
45        TypeId::of::<D>() == self.type_id()
46    }
47    /// Returns some reference to the boxed value if it is of type `D`, or
48    /// `None` if it isn't.
49    pub fn downcast_ref<D: NamedBusDevice<T> + 'static>(&self) -> Option<&D> {
50        if self.is::<D>() {
51            unsafe {
52                Some(&*(self as *const dyn NamedBusDevice<T> as *const D))
53            }
54        } else {
55            None
56        }
57    }
58    /// Returns some mutable reference to the boxed value if it is of type `D`, or
59    /// `None` if it isn't.
60    pub fn downcast_mut<D: NamedBusDevice<T> + 'static>(&mut self) -> Option<&mut D> {
61        if self.is::<D>() {
62            unsafe {
63                Some(&mut *(self as *mut dyn NamedBusDevice<T> as *mut D))
64            }
65        } else {
66            None
67        }
68    }
69}
70
71/// An interface for emulating devices that communicate with the emulated `CPU` via I/O requests.
72///
73/// This trait allows attaching many, different devices to form a so-called "daisy chain".
74///
75/// Implementations of this trait should be provided as an associated type of [ControlUnit::BusDevice].
76///
77/// [ControlUnit]: crate::chip::ControlUnit
78/// [ControlUnit::BusDevice]: crate::chip::ControlUnit::BusDevice
79pub trait BusDevice: Debug {
80    /// A type used as a time-stamp.
81    type Timestamp: Sized;
82    /// A type of the next device in a daisy chain.
83    type NextDevice: BusDevice<Timestamp=Self::Timestamp>;
84
85    /// Returns a mutable reference to the next device.
86    fn next_device_mut(&mut self) -> &mut Self::NextDevice;
87    /// Returns a reference to the next device.
88    fn next_device_ref(&self) -> &Self::NextDevice;
89    /// Destructs self and returns the instance of the next bus device.
90    fn into_next_device(self) -> Self::NextDevice;
91    /// Resets the device and all devices in this chain.
92    ///
93    /// This method is called from [ControlUnit::reset][crate::chip::ControlUnit::reset].
94    ///
95    /// Default implementation forwards this call to the next device.
96    ///
97    /// **NOTE**: Implementations should always forward this call down the chain after optionally applying it
98    /// to `self`.
99    #[inline(always)]
100    fn reset(&mut self, timestamp: Self::Timestamp) {
101        self.next_device_mut().reset(timestamp)
102    }
103    /// This method should be called near the end of each frame.
104    ///
105    /// Default implementation forwards this call to the next device.
106    ///
107    /// **NOTE**: Implementations should always forward this call down the chain after optionally applying it
108    /// to `self`.
109    ///
110    /// [ControlUnit::execute_next_frame]: crate::chip::ControlUnit::execute_next_frame
111    #[inline(always)]
112    fn update_timestamp(&mut self, timestamp: Self::Timestamp) {
113        self.next_device_mut().update_timestamp(timestamp)
114    }
115    /// This method should be called just before the T-state counter of the control unit is wrapped when preparing
116    /// for the next frame.
117    ///
118    /// It allows the devices that are tracking time to adjust stored timestamps accordingly by subtracting
119    /// the total number of T-states per frame from the stored ones. The `eof_timestamp` argument indicates
120    /// the total number of T-states in a single frame.
121    ///
122    /// Optionally enables implementations to perform an end-of-frame action.
123    ///
124    /// Default implementation forwards this call to the next device.
125    ///
126    /// **NOTE**: Implementations should always forward this call down the chain after optionally applying it
127    /// to `self`.
128    #[inline(always)]
129    fn next_frame(&mut self, eof_timestamp: Self::Timestamp) {
130        self.next_device_mut().next_frame(eof_timestamp)
131    }
132    /// This method is called by the control unit during an I/O read cycle.
133    ///
134    /// Default implementation forwards this call to the next device.
135    ///
136    /// Returns an optional tuple with the (data, insert wait states).
137    ///
138    /// **NOTE**: Implementations should only need to forward this call if it does not apply to this device
139    /// or if not all bits are modified by the implementing device. In the latter case the result from the
140    /// forwarded call should be logically `ANDed` with the result of reading from this device and if the
141    /// upstream result is `None` the result should be returned with all unused bits set to 1.
142    #[inline(always)]
143    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
144        self.next_device_mut().read_io(port, timestamp)
145    }
146    /// This method is called by the control unit during an I/O write cycle.
147    ///
148    /// Returns `Some(insert wait states)` if the device has blocked writing through it.
149    ///
150    /// Default implementation forwards this call to the next device.
151    ///
152    /// **NOTE**: Implementations should only forward this call to the next device if it does not apply
153    /// to this device or if the device doesn't block writing. If the device blocks writing to downstream
154    /// devices and the port matches, this method must return `true`. Otherwise this method should return
155    /// the forwarded result.
156    #[inline(always)]
157    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
158        self.next_device_mut().write_io(port, data, timestamp)
159    }
160    /// Gets the `TypeId` of `self`.
161    ///
162    /// A required part for the ability to downcast dynamic `BusDevice` instances.
163    ///
164    /// # Safety
165    /// The default implementation of this method must not be overwritten by the specializations.
166    /// Consider this method as `final`.
167    fn type_id(&self) -> TypeId where Self: 'static {
168        TypeId::of::<Self>()
169    }
170}
171
172impl<D: BusDevice> BusDevice for Box<D> {
173    type Timestamp = D::Timestamp;
174    type NextDevice = D::NextDevice;
175
176    #[inline(always)]
177    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
178        (**self).next_device_mut()
179    }
180    #[inline(always)]
181    fn next_device_ref(&self) -> &Self::NextDevice {
182        (**self).next_device_ref()
183    }
184    #[inline]
185    fn into_next_device(self) -> Self::NextDevice {
186        (*self).into_next_device()
187    }
188    #[inline]
189    fn reset(&mut self, timestamp: Self::Timestamp) {
190        (**self).reset(timestamp)
191    }
192    #[inline]
193    fn update_timestamp(&mut self, timestamp: Self::Timestamp) {
194        (**self).update_timestamp(timestamp)
195    }
196    #[inline]
197    fn next_frame(&mut self, eof_timestamp: Self::Timestamp) {
198        (**self).next_frame(eof_timestamp)
199    }
200    #[inline]
201    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
202        (**self).read_io(port, timestamp)
203    }
204    #[inline]
205    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
206        (**self).write_io(port, data, timestamp)
207    }
208}
209
210/// A helper trait for matching I/O port addresses.
211pub trait PortAddress: Debug {
212    /// Relevant address bits should be set to 1.
213    const ADDRESS_MASK: u16;
214    /// Bits from this constant will be matching only if `ADDRESS_MASK` constains 1 for bits in the same positions.
215    const ADDRESS_BITS: u16;
216    /// Returns `true` if a provided `address` masked with `ADDRESS_MASK` matches `ADDRESS_BITS`.
217    #[inline]
218    fn match_port(address: u16) -> bool {
219        address & Self::ADDRESS_MASK == Self::ADDRESS_BITS & Self::ADDRESS_MASK
220    }
221}
222
223/// A daisy-chain terminator device. Use it as the last device in a chain.
224///
225/// Substitute `T` with a timestamp type.
226#[derive(Clone, PartialEq, Eq)]
227#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
228pub struct NullDevice<T>(PhantomData<T>);
229
230pub type VFNullDevice<V> = NullDevice<VFrameTs<V>>;
231
232impl<T> Default for NullDevice<T> {
233    #[inline(always)]
234    fn default() -> Self {
235        NullDevice(PhantomData)
236    }
237}
238
239impl<T> BusDevice for NullDevice<T> {
240    type Timestamp = T;
241    type NextDevice = Self;
242
243    #[inline(always)]
244    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
245        self
246    }
247    #[inline(always)]
248    fn next_device_ref(&self) -> &Self::NextDevice {
249        self
250    }
251    #[inline(always)]
252    fn into_next_device(self) -> Self::NextDevice {
253        self
254    }
255    #[inline(always)]
256    fn reset(&mut self, _timestamp: Self::Timestamp) {}
257
258    #[inline(always)]
259    fn update_timestamp(&mut self, _timestamp: Self::Timestamp) {}
260
261    #[inline(always)]
262    fn next_frame(&mut self, _timestamp: Self::Timestamp) {}
263
264    #[inline(always)]
265    fn read_io(&mut self, _port: u16, _timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
266        None
267    }
268
269    #[inline(always)]
270    fn write_io(&mut self, _port: u16, _data: u8, _timestamp: Self::Timestamp) -> Option<u16> {
271        None
272    }
273}
274
275impl<T> fmt::Debug for NullDevice<T> {
276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277        f.debug_struct("NullDevice").finish()
278    }
279}
280
281impl<T> fmt::Display for NullDevice<T> {
282    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        Ok(())
284    }
285}
286
287/// A pseudo [BusDevice] allowing for plugging in and out a device at run time.
288#[derive(Clone, Default, Debug)]
289#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
290#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
291pub struct OptionalBusDevice<D, N> {
292    /// The device that can be "plugged in".
293    #[cfg_attr(feature = "snapshot", serde(default))]
294    pub device: Option<D>,
295    /// The next device in the daisy chain.
296    #[cfg_attr(feature = "snapshot", serde(default))]
297    pub next_device: N
298}
299
300impl<D, N> OptionalBusDevice<D, N>
301    where D: BusDevice, N: BusDevice
302{
303    pub fn new(device: Option<D>, next_device: N) -> Self {
304        OptionalBusDevice { device, next_device }
305    }
306}
307
308impl<D, N> Deref for OptionalBusDevice<D, N> {
309    type Target = Option<D>;
310    fn deref(&self) -> &Self::Target {
311        &self.device
312    }
313}
314
315impl<D, N> DerefMut for OptionalBusDevice<D, N> {
316    fn deref_mut(&mut self) -> &mut Self::Target {
317        &mut self.device
318    }
319}
320
321impl<D, N> BusDevice for OptionalBusDevice<D, N>
322    where D: BusDevice,
323          N: BusDevice<Timestamp=D::Timestamp>,
324          D::Timestamp: Copy
325{
326    type Timestamp = D::Timestamp;
327    type NextDevice = N;
328
329    #[inline]
330    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
331        &mut self.next_device
332    }
333    #[inline]
334    fn next_device_ref(&self) -> &Self::NextDevice {
335        &self.next_device
336    }
337    #[inline]
338    fn into_next_device(self) -> Self::NextDevice {
339        self.next_device
340    }
341    #[inline]
342    fn reset(&mut self, timestamp: Self::Timestamp) {
343        if let Some(device) = &mut self.device {
344            device.reset(timestamp);
345        }
346        self.next_device.reset(timestamp);
347    }
348    #[inline]
349    fn update_timestamp(&mut self, timestamp: Self::Timestamp) {
350        if let Some(device) = &mut self.device {
351            device.update_timestamp(timestamp);
352        }
353        self.next_device.update_timestamp(timestamp);
354    }
355    #[inline]
356    fn next_frame(&mut self, timestamp: Self::Timestamp) {
357        if let Some(device) = &mut self.device {
358            device.next_frame(timestamp);
359        }
360        self.next_device.next_frame(timestamp);
361    }
362    #[inline]
363    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
364        let dev_data = if let Some((data, ws)) = self.device
365                            .as_mut()
366                            .and_then(|dev| dev.read_io(port, timestamp)) {
367            if ws.is_some() {
368            // we assume a halting device takes highest priority in this request
369                return Some((data, ws))
370            }
371            Some(data)
372        }
373        else {
374            None
375        };
376        if let Some((bus_data, ws)) = self.next_device.read_io(port, timestamp) {
377            let data = bus_data & dev_data.unwrap_or(!0);
378            return Some((data, ws))
379        }
380        dev_data.map(|data| (data, None))
381    }
382
383    #[inline]
384    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
385        if let Some(device) = &mut self.device {
386            if let Some(ws) = device.write_io(port, data, timestamp) {
387                return Some(ws)
388            }
389        }
390        self.next_device.write_io(port, data, timestamp)
391    }
392}
393
394impl<D, N> fmt::Display for OptionalBusDevice<D, N>
395    where D: fmt::Display
396{
397    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
398        if let Some(ref device) = self.device {
399            device.fmt(f)
400        }
401        else {
402            Ok(())
403        }
404    }
405}