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}