spectrusty_peripherals/bus/
parallel.rs1use core::num::NonZeroU16;
10use core::fmt;
11use core::ops::{Deref, DerefMut};
12
13#[cfg(feature = "snapshot")]
14use serde::{Serialize, Deserialize};
15
16use spectrusty_core::{
17 bus::{BusDevice, PortAddress},
18};
19use super::ay::PassByAyAudioBusDevice;
20
21pub use crate::parallel::*;
22
23pub type Plus3CentronicsWriterBusDevice<D, W> = Plus3CentronicsBusDevice<ParallelPortWriter<
24 <D as BusDevice>::Timestamp, W>, D>;
25
26impl<P, D> fmt::Display for Plus3CentronicsBusDevice<P, D> {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.write_str("+3 Centronics Port")
29 }
30}
31
32bitflags! {
33 #[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
34 #[cfg_attr(feature = "snapshot", serde(from = "u8", into = "u8"))]
35 #[derive(Default)]
36 struct CentronicsFlags: u8 {
37 const BUSY = 0b0000_0001;
38 const STROBE = 0b0001_0000;
39 }
40}
41
42impl CentronicsFlags {
43 fn set_busy(&mut self, busy: bool) {
44 self.set(CentronicsFlags::BUSY, busy)
45 }
46
47 fn is_busy(self) -> bool {
48 self.intersects(CentronicsFlags::BUSY)
49 }
50
51 fn is_strobe(self) -> bool {
52 self.intersects(CentronicsFlags::STROBE)
53 }
54}
55
56#[derive(Clone, Default, Debug)]
60#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
61pub struct Plus3CentronicsBusDevice<P, D>
62{
63 #[cfg_attr(feature = "snapshot", serde(default))]
65 pub parallel: P,
66 flags: CentronicsFlags,
67 #[cfg_attr(feature = "snapshot", serde(default))]
68 bus: D
69}
70
71#[derive(Clone, Copy, Default, Debug)]
72struct Ula3CtrlPortAddress;
73impl PortAddress for Ula3CtrlPortAddress {
74 const ADDRESS_MASK: u16 = 0b1111_0000_0000_0010;
75 const ADDRESS_BITS: u16 = 0b0001_1111_1111_1101;
76}
77
78#[derive(Clone, Copy, Default, Debug)]
79struct Ula3CentronicsPortAddress;
80impl PortAddress for Ula3CentronicsPortAddress {
81 const ADDRESS_MASK: u16 = 0b1111_0000_0000_0010;
82 const ADDRESS_BITS: u16 = 0b0000_1111_1111_1101;
83}
84
85impl<P, D> Deref for Plus3CentronicsBusDevice<P, D> {
86 type Target = P;
87 fn deref(&self) -> &Self::Target {
88 &self.parallel
89 }
90}
91
92impl<P, D> DerefMut for Plus3CentronicsBusDevice<P, D> {
93 fn deref_mut(&mut self) -> &mut Self::Target {
94 &mut self.parallel
95 }
96}
97
98impl<P, D> PassByAyAudioBusDevice for Plus3CentronicsBusDevice<P, D> {}
99
100impl<P, D> BusDevice for Plus3CentronicsBusDevice<P, D>
101 where P: ParallelPortDevice<Timestamp=D::Timestamp> + fmt::Debug,
102 D: BusDevice,
103 D::Timestamp: Copy
104{
105 type Timestamp = D::Timestamp;
106 type NextDevice = D;
107
108 #[inline]
109 fn next_device_mut(&mut self) -> &mut Self::NextDevice {
110 &mut self.bus
111 }
112
113 #[inline]
114 fn next_device_ref(&self) -> &Self::NextDevice {
115 &self.bus
116 }
117
118 #[inline]
119 fn into_next_device(self) -> Self::NextDevice {
120 self.bus
121 }
122
123 #[inline]
124 fn reset(&mut self, timestamp: Self::Timestamp) {
125 self.bus.reset(timestamp);
126 }
127
128 #[inline]
129 fn next_frame(&mut self, eof_timestamp: Self::Timestamp) {
130 self.parallel.next_frame(eof_timestamp);
131 self.bus.next_frame(eof_timestamp)
132 }
133
134 #[inline]
135 fn update_timestamp(&mut self, timestamp: Self::Timestamp) {
136 if self.flags.is_busy() {
137 self.flags.set_busy( self.parallel.poll_busy() );
138 };
139 self.bus.update_timestamp(timestamp)
140 }
141
142 #[inline]
143 fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
144 if Ula3CentronicsPortAddress::match_port(port) {
145 let data = (self.flags & CentronicsFlags::BUSY).bits() | !CentronicsFlags::BUSY.bits();
146 return Some((data, None))
147 }
148 self.bus.read_io(port, timestamp)
149 }
150
151 #[inline]
152 fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
153 if Ula3CentronicsPortAddress::match_port(port) {
154 self.parallel.write_data(data, timestamp);
156 return Some(0);
157 }
158 else if Ula3CtrlPortAddress::match_port(port) {
159 let flags = CentronicsFlags::from_bits_truncate(data);
160 let flags_diff = (self.flags ^ flags) & CentronicsFlags::STROBE;
161 if flags_diff == CentronicsFlags::STROBE {
162 self.flags ^= flags_diff;
163 self.flags.set_busy(
164 self.parallel.write_strobe(flags.is_strobe(), timestamp));
165 }
166 }
167 self.bus.write_io(port, data, timestamp)
168 }
169}
170
171impl From<CentronicsFlags> for u8 {
172 #[inline]
173 fn from(flags: CentronicsFlags) -> u8 {
174 flags.bits()
175 }
176}
177
178impl From<u8> for CentronicsFlags {
179 #[inline]
180 fn from(flags: u8) -> CentronicsFlags {
181 CentronicsFlags::from_bits_truncate(flags)
182 }
183}