spectrusty_peripherals/bus/
zxinterface1.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 **ZX Interface I**.
9/*!
10
11### I/O Port **0xe7**.
12
13Used to send or receive data to and from the microdrive.
14
15Accessing this port will halt the Z80 until the Interface I has collected 8 bits from the microdrive head;
16therefore, if the microdrive motor isn't running, or there is no formatted cartridge in the microdrive,
17the Spectrum hangs. This is the famous 'IN 0 crash'.
18
19### I/O Port **0xef**.
20
21Bits *DTR* and *CTS* are used by the RS-232 interface.
22
23The *WAIT* bit is used by the ZX Network to synchronize incoming bytes.
24It is being activated just before reading bits of each incoming byte.
25
26*GAP*, *SYNC*, *WR_PROT*, *ERASE*, *R/W*, *COMMS CLK*, and *COMMS DATA* are used by the microdrive system.
27
28```text
29       Bit    7   6    5    4    3    2    1     0
30            +---------------------------------------+
31        READ|   |   |    |busy| dtr |gap| sync|write|
32            |   |   |    |    |     |   |     |prot.|
33            |---+---+----+----+-----+---+-----+-----|
34       WRITE|   |   |wait| cts|erase|r/w|comms|comms|
35            |   |   |    |    |     |   | clk | data|
36            +---------------------------------------+
37```
38### I/O Port **0xf7**.
39
40If the microdrive is not being used, the *COMMS DATA* output selects the function of OUT bit 0 of **0xf7** port:
41```text
42       Bit      7    6   5   4   3   2   1       0
43            +------------------------------------------+
44        READ|txdata|   |   |   |   |   |   |    net    |
45            |      |   |   |   |   |   |   |   input   |
46            |------+---+---+---+---+---+---+-----------|
47       WRITE|      |   |   |   |   |   |   |net output/|
48            |      |   |   |   |   |   |   |   rxdata  |
49            +------------------------------------------+
50```
51If a 0 was written to *COMMS DATA* the lowest bit is written to the *net output*, if 1 was written to *COMMS DATA*
52the lowest written bit is *rxdata*.
53!*/
54use core::num::NonZeroU16;
55use core::fmt;
56use std::io;
57
58#[cfg(feature = "snapshot")]
59use serde::{Serialize, Deserialize};
60
61use spectrusty_core::{
62    clock::TimestampOps,
63    bus::BusDevice
64};
65
66use super::ay::PassByAyAudioBusDevice;
67
68pub use crate::{
69    storage::microdrives::*,
70    serial::{SerialPortDevice, DataState, ControlState, Rs232Io, BAUD_RATES, DEFAULT_BAUD_RATE},
71    network::zxnet::*
72};
73
74impl<R, W, N, D: BusDevice> fmt::Display for ZxInterface1BusDevice<R, W, N, D> {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        f.write_str("ZX Interface I")
77    }
78}
79
80/// Connects the ZX Interface 1 I/O port emulator as a [BusDevice].
81///
82/// [Rs232Io]'s [io::Read] as `R` and [io::Write] as `W` implementations are needed to complete this type.
83/// [ZxNetSocket] implementation is needed as `N` for the underlying [ZxNet].
84#[derive(Default, Debug)]
85#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
86#[cfg_attr(feature = "snapshot", serde(bound(deserialize = "
87    R: Deserialize<'de> + Default,
88    W: Deserialize<'de> + Default,
89    N: Default,
90    D: Deserialize<'de> + Default,
91    D::Timestamp: Deserialize<'de> + Default",
92serialize = "
93    R: Serialize,
94    W: Serialize,
95    D: Serialize,
96    D::Timestamp: Serialize")))]
97#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
98pub struct ZxInterface1BusDevice<R, W, N, D: BusDevice>
99{
100    /// Direct access to the **Microdrives**.
101    #[cfg_attr(feature = "snapshot", serde(default))]
102    pub microdrives: ZxMicrodrives<D::Timestamp>,
103    /// Direct access to the **RS-232** implementation.
104    #[cfg_attr(feature = "snapshot", serde(default))]
105    pub serial: Rs232Io<D::Timestamp, R, W>,
106    /// Direct access to the **ZX NET** implementation.
107    #[cfg_attr(feature = "snapshot", serde(skip))]
108    pub network: ZxNet<D::Timestamp, N>,
109    sernet: If1SerNetIo,
110    ctrl_in: If1ControlIn,
111    ctrl_out: If1ControlOut,
112    #[cfg_attr(feature = "snapshot", serde(default))]
113    bus: D
114}
115
116bitflags! {
117    #[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
118    struct If1SerNetIo: u8 {
119        const RXD     = 0b0000_0001;
120        const TXD     = 0b1000_0000;
121        const NET     = 0b0000_0001;
122        const DEFAULT = 0b0111_1110;
123    }
124}
125
126bitflags! {
127    #[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
128    struct If1ControlIn: u8 {
129        const MD_MASK  = 0b0000_0111;
130        const MD_PROT  = 0b0000_0001;
131        const MD_SYN   = 0b0000_0010;
132        const MD_GAP   = 0b0000_0100;
133        const SER_DTR  = 0b0000_1000;
134        const NET_BUSY = 0b0001_0000;
135        const DEFAULT  = 0b1110_0111;
136    }
137}
138
139bitflags! {
140    #[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
141    struct If1ControlOut: u8 {
142        const MD_MASK   = 0b0000_1111;
143        const COMMS_OUT = 0b0000_0001;
144        const COMMS_CLK = 0b0000_0010;
145        const MD_R_W    = 0b0000_0100;
146        const MD_ERASE  = 0b0000_1000;
147        const SER_CTS   = 0b0001_0000;
148        const NET_WAIT  = 0b0010_0000;
149        const DEFAULT   = 0b1110_1110;
150    }
151}
152
153macro_rules! default_io {
154    ($($ty:ty),*) => {$(
155        impl Default for $ty {
156            fn default() -> Self {
157                <$ty>::DEFAULT
158            }
159        }
160    )*};
161}
162
163default_io!(If1ControlOut, If1ControlIn, If1SerNetIo);
164
165impl If1SerNetIo {
166    #[inline(always)]
167    fn rxd(self) -> DataState {
168        DataState::from(!self.intersects(If1SerNetIo::RXD))
169    }
170    #[inline(always)]
171    fn net(self) -> bool {
172        !self.intersects(If1SerNetIo::NET)
173    }
174    #[inline(always)]
175    fn set_txd(&mut self, txd: DataState) {
176        self.set(If1SerNetIo::TXD, !bool::from(txd))
177    }
178    #[inline(always)]
179    fn set_net(&mut self, net: bool) {
180        self.set(If1SerNetIo::NET, net)
181    }
182}
183
184impl If1ControlIn {
185    #[inline(always)]
186    fn set_md_state(&mut self, CartridgeState { gap, syn, write_protect }: CartridgeState) {
187        self.insert(If1ControlIn::MD_MASK);
188        if gap {
189            self.remove(If1ControlIn::MD_GAP);
190        }
191        if syn {
192            self.remove(If1ControlIn::MD_SYN);
193        }
194        if write_protect {
195            self.remove(If1ControlIn::MD_PROT);
196        }
197    }
198    #[inline(always)]
199    fn set_dtr(&mut self, dtr: ControlState) {
200        self.set(If1ControlIn::SER_DTR, !bool::from(dtr))
201    }
202}
203
204impl If1ControlOut {
205    #[inline(always)]
206    fn is_comms_out_ser(self) -> bool {
207        self.intersects(If1ControlOut::COMMS_OUT)
208    }
209    #[inline(always)]
210    fn erase(self) -> bool {
211        !self.intersects(If1ControlOut::MD_ERASE)
212    }
213    #[inline(always)]
214    fn write(self) -> bool {
215        !self.intersects(If1ControlOut::MD_R_W)
216    }
217    #[inline(always)]
218    fn comms_clk(self) -> bool {
219        !self.intersects(If1ControlOut::COMMS_CLK)
220    }
221    #[inline(always)]
222    fn comms_out(self) -> bool {
223        !self.intersects(If1ControlOut::COMMS_OUT)
224    }
225    #[inline(always)]
226    fn cts(self) -> ControlState {
227        ControlState::from(!self.intersects(If1ControlOut::SER_CTS))
228    }
229    #[inline(always)]
230    fn wait(self) -> bool {
231        !self.intersects(If1ControlOut::NET_WAIT)
232    }
233}
234
235const IF1_MASK:      u16 = 0b0000_0000_0001_1000;
236const IF1_SERN_BITS: u16 = 0b0000_0000_0001_0000;
237const IF1_CTRL_BITS: u16 = 0b0000_0000_0000_1000;
238const IF1_DATA_BITS: u16 = 0b0000_0000_0000_0000;
239
240impl<R, W, N, D: BusDevice> PassByAyAudioBusDevice for ZxInterface1BusDevice<R, W, N, D> {}
241
242impl<R, W, N, D> BusDevice for ZxInterface1BusDevice<R, W, N, D>
243    where R: io::Read + fmt::Debug,
244          W: io::Write + fmt::Debug,
245          N: ZxNetSocket + fmt::Debug,
246          D: BusDevice,
247          D::Timestamp: TimestampOps
248{
249    type Timestamp = D::Timestamp;
250    type NextDevice = D;
251
252    #[inline]
253    fn next_device_mut(&mut self) -> &mut Self::NextDevice {
254        &mut self.bus
255    }
256
257    #[inline]
258    fn next_device_ref(&self) -> &Self::NextDevice {
259        &self.bus
260    }
261
262    #[inline]
263    fn into_next_device(self) -> Self::NextDevice {
264        self.bus
265    }
266
267    #[inline]
268    fn reset(&mut self, timestamp: Self::Timestamp) {
269        self.microdrives.reset(timestamp);
270        self.bus.reset(timestamp);
271    }
272
273    #[inline]
274    fn update_timestamp(&mut self, timestamp: Self::Timestamp) {
275        self.microdrives.update_timestamp(timestamp);
276        self.ctrl_in.set_dtr(self.serial.poll_ready(timestamp));
277    }
278
279    #[inline]
280    fn next_frame(&mut self, eof_timestamp: Self::Timestamp) {
281        // println!("frame ends");
282        self.microdrives.next_frame(eof_timestamp);
283        self.serial.next_frame(eof_timestamp);
284        self.network.next_frame(eof_timestamp);
285        self.bus.next_frame(eof_timestamp)
286    }
287
288    #[inline]
289    fn read_io(&mut self, port: u16, timestamp: Self::Timestamp) -> Option<(u8, Option<NonZeroU16>)> {
290        match port & IF1_MASK {
291            IF1_SERN_BITS => {
292                self.sernet.set_txd(self.serial.read_data(timestamp));
293                if self.ctrl_out.cts().is_inactive() {
294                    self.sernet.set_net(self.network.poll_state(timestamp));
295                }
296                // println!("net read: {:02x} {:?}", self.sernet.bits(), timestamp);
297                Some((self.sernet.bits(), None))
298            }
299            IF1_CTRL_BITS => {
300                self.ctrl_in.set_md_state(self.microdrives.read_state(timestamp));
301                // println!("ctrl read: {:02x} {:?}", self.ctrl_in.bits(), timestamp);
302                // TODO: poll busy?
303                Some((self.ctrl_in.bits(), None))
304            }
305            IF1_DATA_BITS => {
306                Some(self.microdrives.read_data(timestamp))
307            }
308            _ => self.bus.read_io(port, timestamp)
309        }
310    }
311
312    #[inline]
313    fn write_io(&mut self, port: u16, data: u8, timestamp: Self::Timestamp) -> Option<u16> {
314        match port & IF1_MASK {
315            IF1_SERN_BITS => {
316                let data = If1SerNetIo::from_bits_truncate(data);
317                if self.ctrl_out.is_comms_out_ser() {
318                    self.ctrl_in.set_dtr(self.serial.write_data(data.rxd(), timestamp));
319                }
320                else {
321                    // println!("net write rxs: {:?} {:?}", (rxd as u8)^1, timestamp);
322                    self.network.send_state(data.net(), timestamp);
323                    // TODO: handle network
324                }
325                Some(0)
326            }
327            IF1_CTRL_BITS => {
328                let data = If1ControlOut::from_bits_truncate(data);
329                let diff = self.ctrl_out ^ data;
330                // println!("ctrl write: {:?}", data);
331                self.ctrl_out ^= diff;
332                if !(diff & If1ControlOut::MD_MASK).is_empty() {
333                    self.microdrives.write_control(timestamp,
334                            data.erase(), data.write(), data.comms_clk(), data.comms_out());
335                }
336                if !(diff & If1ControlOut::SER_CTS).is_empty() {
337                    self.serial.update_cts(data.cts(), timestamp);
338                }
339                if data.wait() {
340                    self.network.wait_data(timestamp);
341                    // TODO: handle network
342                }
343                Some(0)
344            }
345            IF1_DATA_BITS => Some(self.microdrives.write_data(data, timestamp)),
346            _ => self.bus.write_io(port, data, timestamp)
347        }
348    }
349}