Skip to main content

spectrusty_peripherals/network/
zxnet.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//! *ZX Net* coders for the ZX Interface 1.
9use core::mem;
10use std::time::{Instant};
11
12#[allow(unused_imports)]
13use log::{error, warn, info, debug, trace};
14
15use spectrusty_core::clock::{FTs, TimestampOps};
16pub use super::zxnet_udp::*;
17
18const CPU_HZ: f32 = 3_500_000.0;
19
20/// This trait is being used by [ZxNet] to send and receive ZX-NET packets to and from remote parties.
21pub trait ZxNetSocket {
22    /// Should return a view of the current packet being composed or received.
23    fn packet_data(&self) -> &[u8];
24    /// Signals that the new packet will be composed for sending.
25    fn begin_packet(&mut self);
26    /// Adds a `byte` to the packet data. Should return the size of the packet data after appending `byte`.
27    fn push_byte(&mut self, byte: u8) -> usize;
28    /// Should return the index of the next byte to be pushed to the outbound data packet.
29    /// This is the same as the byte size of the composed data packet so far.
30    fn outbound_index(&self) -> usize;
31    /// Should send the composed packet to the remote party.
32    fn send_packet(&mut self);
33    /// Should optionally wait and get the confirmation from the remote party.
34    /// Returns `true` if the remote party confirmed the received packet.
35    fn recv_accept(&mut self) -> bool;
36    /// Should receive a data packet from the remote party.
37    /// Returns `true` if the remote party has sent the next data packet.
38    fn recv_packet(&mut self) -> bool;
39    /// Gets the next byte from the last received data packet.
40    /// Returns `None` if there are no more bytes to be returned.
41    fn pull_byte(&mut self) -> Option<u8>;
42    /// Should return the index of the next byte to be pulled from the outbound data packet.
43    fn inbound_index(&self) -> usize;
44    /// Should send the confirmation of the received packet to the remote party.
45    fn send_accept(&mut self);
46}
47
48/// Implementation of this struct decodes and encodes ZX-NET packets from Spectrum's I/O port signals.
49///
50/// An implementation of [ZxNetSocket] should be provided as its `S` type parameter.
51#[derive(Debug)]
52pub struct ZxNet<T, S> {
53    /// Direct access to the underlying [ZxNetSocket] implementation.
54    pub socket: S,
55    event_ts: T,
56    dir_io: NetDir,
57    io: NetState,
58    net_state: bool
59}
60
61/// A helper struct for reading ZX-NET header information.
62#[repr(C, packed)]
63pub struct ZxNetHead {
64    /// `NCIRIS` The destination station number.
65    pub dest: u8,       
66    /// `NCSELF` This Spectrum's station number.
67    pub ours: u8,       
68    /// `NCNUMB` The block number.
69    pub serial: [u8;2], 
70    /// `NCTYPE` The packet type code . 0 data, 1 EOF
71    pub eof: u8,        
72    /// `NCOBL` Number of bytes in data block.
73    pub size: u8,       
74    /// `NCDCS` The data checksum.
75    pub dchk: u8,       
76    /// `NCHCS` The header checksum.
77    pub hchk: u8,       
78}
79
80pub(super) const HEAD_SIZE: usize = mem::size_of::<ZxNetHead>();
81
82/// A trait for converting data to references of [ZxNetHead].
83pub trait DataAsZxNetHead {
84    /// Converts a reference to a data packet to a reference of [ZxNetHead].
85    fn as_zxnet_header(&self) -> &ZxNetHead;
86}
87
88impl DataAsZxNetHead for [u8] {
89    /// # Panics
90    /// Panics if the slice length is less than the size of the [ZxNetHead] struct.
91    fn as_zxnet_header(&self) -> &ZxNetHead {
92        let head = &self[..mem::size_of::<ZxNetHead>()];
93        let ptr = head.as_ptr() as *const ZxNetHead;
94        unsafe { &*ptr }
95    }
96}
97
98// const REST_DELAY: u32 = 6912 + 250; // input
99// const SCOUT_WAIT_MAX: u32 = 47250;
100const INPAK_WAIT_MAX: FTs = 8925;
101const OUTPAK_START_DELAY: FTs = 110;
102const BIT_DELAY: FTs = 60;
103const REST_DELAY_THRESHOLD: FTs = 64;
104const PROBE_DELAY_MIN: FTs = 65;
105const PROBE_DELAY_MAX: FTs = 130;
106const BYTE_DELAY: FTs = 120;
107const BROADCAST_DATA_DELAY: i32 = 530;
108
109// const MAX_PACKET_SIZE: usize = 256 + BODY_INDEX;
110
111#[derive(Clone, Copy, Debug, PartialEq)]
112enum NetDir {
113    Inbound,
114    Outbound
115}
116
117#[derive(Clone, Copy, Debug, PartialEq)]
118enum NetState {
119    Idle(u8),
120    InputScout,
121    InputStart,
122    InputData(u8),
123    OutputScout,
124    OutputStart,
125    OutputData(u8),
126    OutputStop(u8),
127    OutputEnd
128}
129// https://scratchpad.fandom.com/wiki/ZX_Net
130// scout: 1 x x x x x x x 0 
131//(2.5ms) 1 [ 0 x x x x x x x x 1 * bytes ] 0
132impl<T: TimestampOps, S: ZxNetSocket> ZxNet<T, S> {
133    pub fn send_state(&mut self, net: bool, timestamp: T) {
134        match self.io {
135            NetState::Idle(..) => {
136                // println!("set scout: {} {} {}", net, V::vts_diff(self.event_ts, timestamp), V::vts_to_tstates(timestamp));
137                self.net_state = net;
138                self.io = NetState::OutputScout;
139            }
140            NetState::OutputScout if net && !self.net_state => { // OUTPAK will start
141                // println!("OUTPAK start from scout");
142                self.socket.begin_packet();
143                self.dir_io = NetDir::Outbound;
144                self.event_ts = timestamp + OUTPAK_START_DELAY;
145                self.io = NetState::OutputStart;
146            }
147            NetState::OutputScout => {
148                // println!("fake scout");
149                self.net_state = false;
150                self.io = NetState::Idle(0);
151            }
152            NetState::InputData(0) if net && timestamp >= self.event_ts => { // reply or body packet out OUTPAK
153                // println!("reply or body packet OUTPAK: {}", V::vts_diff(self.event_ts, timestamp));
154                self.event_ts = timestamp + OUTPAK_START_DELAY;
155                self.io = NetState::OutputStart;
156            }
157            NetState::OutputStart if !net && timestamp < self.event_ts => {
158                // println!("OUTPAK start: {}", V::vts_diff(self.event_ts, timestamp));
159                self.event_ts = timestamp + BIT_DELAY;
160                self.io = NetState::OutputData(0x80);
161            }
162            NetState::OutputData(bits) if timestamp < self.event_ts => {
163                let next_bits = ((bits & !1)| u8::from(net)).rotate_right(1);
164                self.event_ts = timestamp + BIT_DELAY;
165                self.io = if bits & 1 == 1 {
166                    NetState::OutputStop(next_bits)
167                }
168                else {
169                    NetState::OutputData(next_bits)
170                };
171            }
172            NetState::OutputStop(byte) if net && timestamp < self.event_ts => {
173                // println!("OUTPAK stop: {:02x}", byte);
174                match self.dir_io {
175                    NetDir::Inbound if byte == 1 => {
176                        // println!("got send resp 1");
177                        if self.socket.inbound_index() == HEAD_SIZE {
178                            // let now = Instant::now();
179                            self.socket.send_accept();
180                            // println!("sent accept in {:?}", now.elapsed());
181                        }
182                        self.io = NetState::OutputEnd;
183                    }
184                    NetDir::Inbound => {
185                        // println!("this should be 1");
186                        self.net_state = false;
187                        self.io = NetState::Idle(0); // end of packet transmission                        
188                    }
189                    NetDir::Outbound => {
190                        let len = self.socket.push_byte(byte);
191                        self.io = if len == HEAD_SIZE {
192                            // println!("outbound header end");
193                            NetState::OutputEnd
194                        }
195                        else if len > HEAD_SIZE
196                             && len - HEAD_SIZE == self.socket.packet_data().as_zxnet_header().size as usize {
197                            // println!("outbound data end");
198                            self.socket.send_packet();
199                            NetState::OutputEnd
200                        }
201                        else {
202                            NetState::OutputStart
203                        }
204                    }
205                }
206                self.event_ts = timestamp + BYTE_DELAY;
207            }
208            NetState::OutputEnd if !net && timestamp < self.event_ts => { // end outpack
209                self.event_ts = timestamp; // TODO: SOME TIMEOUT
210                let head = self.socket.packet_data().as_zxnet_header();
211                match self.dir_io {
212                    NetDir::Inbound => { // end of outpak resp
213                        self.io = if self.socket.inbound_index() == head.size as usize + HEAD_SIZE {
214                            self.net_state = false;
215                            // println!("end of response and transmission");
216                            NetState::Idle(0) // end of packet transmission                        
217                        }
218                        else {
219                            // println!("end of response expecting more data");
220                            self.net_state = true;
221                            NetState::InputScout // expect DATA
222                        }
223                    }
224                    NetDir::Outbound => {
225                        self.io = if self.socket.outbound_index() == head.size as usize + HEAD_SIZE {
226                            if head.dest == 0 { // BROADCAST - no response checking
227                                // println!("end of BROADCAST");
228                                self.net_state = false;
229                                NetState::Idle(0) // end of packet transmission
230                            }
231                            else {
232                                // println!("check remote response");
233                                self.net_state = false;
234                                NetState::InputScout // expect CHECK RESP need to fetch acceptance
235                            }
236                        }
237                        else if head.dest == 0 { // BROADCAST - no response checking
238                            // println!("BROADCAST: no check");
239                            self.net_state = false;
240                            NetState::InputData(0) // expect next OUTPAK
241                        }
242                        else {
243                            // println!("check immediate response");
244                            self.net_state = true;
245                            NetState::InputScout // expect CHECK RESP
246                        }
247                    }
248                }
249            }
250            _ => { // something wrong, let scout routine know it's not the right moment
251                // println!("OUTPUT something wrong: {:?} {:?} {} {}", net, self.io, V::vts_to_tstates(timestamp), V::vts_diff(self.event_ts, timestamp));
252                self.io = NetState::Idle(0);
253                self.net_state = false;
254            }
255        }
256    }
257
258    pub fn poll_state(&mut self, timestamp: T) -> bool {
259        match self.io {
260            NetState::Idle(cnt) => {
261                if timestamp >= self.event_ts {
262                    match timestamp.diff_from(self.event_ts) as FTs {
263                        0..=REST_DELAY_THRESHOLD => {
264                            // println!("IDLE REST: {} {}", V::vts_diff(self.event_ts, timestamp), cnt);
265                            self.event_ts = timestamp;
266                            self.io = NetState::Idle(cnt.saturating_add(1));
267                        }
268                        PROBE_DELAY_MIN..=PROBE_DELAY_MAX if cnt < 191 => { // WAIT SCOUT
269                            // let now = Instant::now();
270                            if self.socket.recv_packet() {
271                                // println!("{} got packet let it REST: {} {:?} {:?}", cnt,
272                                //             self.socket.packet_data().len(), now.elapsed(), &self.socket.packet_data()[0..8]);
273                                // got a packet, so regardless of what spectrums wants we will try to shove it
274                                self.event_ts = timestamp;
275                                self.io = NetState::InputScout;
276                                self.dir_io = NetDir::Inbound;
277                                self.net_state = true;
278                            }
279                            else {
280                                // println!("READ PROBE: {}", V::vts_diff(self.event_ts, timestamp));
281                                self.event_ts = timestamp;
282                            }
283                        }
284                        _ => {
285                            self.event_ts = timestamp;
286                            self.io = NetState::Idle(0);
287                            self.net_state = false;
288                        }
289                    }
290                }
291                else if cnt != 0 {
292                    self.io = NetState::Idle(0);
293                }
294            }
295            NetState::OutputScout => {
296                // println!("verify scout");
297                self.io = NetState::Idle(0);
298            }
299            NetState::InputScout if self.net_state => { // Spectrum ignores scout (maybe set a scout grace time...)
300                // println!("SCOUT -> INPAK: {}", V::vts_diff(self.event_ts, timestamp));
301                self.event_ts = timestamp;
302                self.io = NetState::InputStart;
303            }
304            NetState::InputScout => {
305                let now = Instant::now();
306                if self.socket.recv_accept() {
307                    // println!("got accept: {} {:?}", V::vts_diff(self.event_ts, timestamp), now.elapsed());
308                    self.event_ts = timestamp;
309                    self.io = NetState::InputStart;
310                    self.net_state = true;
311                }
312                else {
313                    // println!("NO ACCEPT!");
314                    self.setup_event_time(timestamp, now);
315                    self.io = NetState::Idle(0);
316                }
317            }
318            NetState::InputStart => { // if this happens, something is wrong, so discard packet and go to idle
319                // println!("INPAK no wait detected!");
320                self.event_ts = timestamp + INPAK_WAIT_MAX;
321                self.io = NetState::Idle(0);
322                self.net_state = false;
323            }
324            NetState::InputData(byte) => {
325                self.net_state = if timestamp < self.event_ts {
326                    self.event_ts = timestamp + BIT_DELAY;
327                    self.io = NetState::InputData(byte >> 1);
328                    byte & 1 == 1
329                }
330                else if timestamp.diff_from(self.event_ts) <= BROADCAST_DATA_DELAY {
331                    // println!("INPUT DATA BROADCAST?: {}", V::vts_diff(self.event_ts, timestamp));
332                    self.event_ts = timestamp;
333                    self.io = NetState::InputStart;
334                    true
335                }
336                else {
337                    // println!("INPUT DATA > IDLE: {}", V::vts_diff(self.event_ts, timestamp));
338                    self.io = NetState::Idle(0);
339                    false
340                };
341            }
342            _ => { // unexpected during output
343                // println!("INPUT something wrong: {:?} {} {}", self.io, V::vts_to_tstates(timestamp), V::vts_diff(self.event_ts, timestamp));
344                self.io = NetState::Idle(0);
345                self.net_state = false;
346            }
347        }
348        self.net_state // set by whatever Spectrum writes
349    }
350
351    pub fn wait_data(&mut self, timestamp: T) {
352        // println!("wait: {}", V::vts_to_tstates(timestamp));
353        if let Some(byte) = match (self.io, self.dir_io) { // Spectrum wants a byte
354                (NetState::InputStart, NetDir::Outbound) => Some(1),
355                (NetState::InputStart, NetDir::Inbound) => self.socket.pull_byte(),
356                (NetState::InputData(0), NetDir::Inbound) if timestamp > self.event_ts &&
357                                        timestamp < self.event_ts + BIT_DELAY => {
358                    // the whole byte has been transerred
359                    self.socket.pull_byte()
360                }
361                _ => None
362            }
363        {
364            // println!("WAIT -> BYTE [{}] {}", byte, V::vts_diff(self.event_ts, timestamp));
365            self.event_ts = timestamp + 2*BIT_DELAY;
366            self.io = NetState::InputData(byte);
367        }
368        else {
369            // println!("bogus WAIT {}", V::vts_diff(self.event_ts, timestamp));
370            self.event_ts = timestamp + INPAK_WAIT_MAX;
371            self.io = NetState::Idle(0);
372            self.net_state = false;
373        }
374    }
375
376    pub fn next_frame(&mut self, eof_timestamp: T) {
377        self.event_ts = self.event_ts.saturating_sub(eof_timestamp);
378    }
379
380    fn setup_event_time(&mut self, timestamp: T, start: Instant) {
381        let elapsed = start.elapsed().as_secs_f32();
382        let elapsed_ts = (elapsed * CPU_HZ).round() as FTs;
383        // println!("waited: {} {}", elapsed_ts, elapsed);
384        self.event_ts = timestamp + elapsed_ts;
385    }
386}
387
388impl<T: Default, S: Default> Default for ZxNet<T, S> {
389    fn default() -> Self {
390        let socket = S::default();
391        let event_ts = T::default();
392        let net_state = false;
393        let dir_io = NetDir::Inbound;
394        let io = NetState::Idle(0);
395        ZxNet { socket, event_ts, net_state, dir_io, io }
396    }
397}