1use core::ops::{Index, IndexMut};
10use core::num::{NonZeroU8, NonZeroU16};
11use core::fmt;
12use core::iter::{Enumerate, FilterMap, Zip, IntoIterator};
13use core::slice;
14use std::vec;
15
16#[cfg(feature = "snapshot")] mod serde;
17#[cfg(feature = "snapshot")]
18use ::serde::{Serialize, Deserialize};
19
20use ::bitvec::prelude::*;
21
22use spectrusty_core::clock::{FTs, TimestampOps};
23
24pub const MAX_HALT_TS: FTs = 2 * BYTE_TS as FTs;
30
31pub const HALT_FOREVER_TS: Option<NonZeroU16> = unsafe {
34 Some(NonZeroU16::new_unchecked(u16::max_value()))
35};
36
37pub type MicroCartridgeSecIter<'a> = FilterMap<
39 Zip<
40 slice::Iter<'a, Sector>,
41 bitvec::slice::Iter<'a, u32, LocalBits>
42 >,
43 &'a dyn Fn(
44 (&'a Sector, BitRef<'a, bitvec::ptr::Const, u32, LocalBits>)
45 ) -> Option<&'a Sector>
46 >;
47
48pub type MicroCartridgeSecIterMut<'a> = FilterMap<
50 Zip<
51 slice::IterMut<'a, Sector>,
52 bitvec::slice::Iter<'a, u32, LocalBits>
53 >,
54 &'a dyn Fn(
55 (&'a mut Sector, BitRef<'a, bitvec::ptr::Const, u32, LocalBits>)
56 ) -> Option<&'a mut Sector>
57 >;
58pub type MicroCartridgeIdSecIter<'a> = FilterMap<
60 Enumerate<
61 Zip<
62 slice::Iter<'a, Sector>,
63 bitvec::slice::Iter<'a, u32, LocalBits>
64 >>,
65 &'a dyn Fn(
66 (usize,
67 (&'a Sector, BitRef<'a, bitvec::ptr::Const, u32, LocalBits>))
68 ) -> Option<(u8, &'a Sector)>
69 >;
70pub const MAX_SECTORS: usize = 256;
72pub const MAX_USABLE_SECTORS: usize = 254;
74pub const MAX_DRIVES: usize = 8;
76
77pub const HEAD_SIZE: usize = 15;
79pub const DATA_SIZE: usize = 528;
81const PREAMBLE_SIZE: u16 = 12;
83
84const BYTE_TS: u32 = 162;
86const HEAD_START_TS: u32 = 1945;
88const HEAD_TS: u32 = 4375; const GAP1_TS: u32 = 13125; const DATA_START_TS: u32 = 1964;
94const DATA_TS: u32 = 87500; const GAP2_TS: u32 = 24500; const SECTOR_TS: u32 = HEAD_TS + GAP1_TS + DATA_TS + GAP2_TS; const SECTOR_MAP_SIZE: usize = (MAX_SECTORS + 31)/32;
102
103#[derive(Clone, Copy)]
105#[repr(C, packed)]
106pub struct Sector {
107 pub head: [u8;HEAD_SIZE],
109 pub data: [u8;DATA_SIZE],
111}
112
113#[derive(Clone)]
118#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
119#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
120pub struct MicroCartridge {
121 sectors: Box<[Sector]>,
122 sector_map: [u32;SECTOR_MAP_SIZE],
123 tape_cursor: TapeCursor,
124 protec: bool,
125 written: Option<NonZeroU16>
126}
127
128#[derive(Clone, Default, Debug)]
132#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
133#[cfg_attr(feature = "snapshot", serde(rename_all = "camelCase"))]
134pub struct ZxMicrodrives<T> {
135 drives: [Option<MicroCartridge>;MAX_DRIVES],
136 write: bool,
137 erase: bool,
138 comms_clk: bool,
139 motor_on_drive: Option<NonZeroU8>,
140 last_ts: T
141}
142
143#[derive(Clone, Copy, Default, Debug, PartialEq)]
144pub(crate) struct CartridgeState {
145 pub gap: bool,
146 pub syn: bool,
147 pub write_protect: bool
148}
149
150#[derive(Clone, Copy, Debug, PartialEq)]
151#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
152enum SecPosition {
153 Preamble1(u16),
154 Header(u16),
155 Gap1,
156 Preamble2(u16),
157 Data(u16),
158 Gap2,
159}
160
161#[derive(Clone, Copy, Debug, PartialEq)]
162#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
163struct TapeCursor {
164 cursor: u32,
165 sector: u8,
166 secpos: SecPosition,
167}
168
169impl Default for Sector {
170 fn default() -> Self {
171 Sector {
172 head: [!0;HEAD_SIZE],
173 data: [!0;DATA_SIZE],
174 }
175 }
176}
177
178impl Default for TapeCursor {
179 fn default() -> Self {
180 TapeCursor {
181 cursor: 0,
182 sector: 0,
183 secpos: SecPosition::Preamble1(0)
184 }
185 }
186}
187
188impl Default for MicroCartridge {
189 fn default() -> Self {
190 MicroCartridge {
191 sectors: vec![Sector::default();MAX_SECTORS].into_boxed_slice(),
192 sector_map: [0;SECTOR_MAP_SIZE],
193 tape_cursor: TapeCursor::default(),
194 protec: false,
195 written: None
196 }
197 }
198}
199
200
201impl fmt::Debug for MicroCartridge {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "MicroCartridge {{ sectors: {}, formatted: {}, protected: {:?}, writing: {:?}, cursor: {:?} }}",
204 self.sectors.len(),
205 self.sector_map.view_bits::<LocalBits>().count_ones(),
206 self.protec,
207 self.written,
208 self.tape_cursor
209 )
210 }
211}
212
213impl MicroCartridge {
214 pub fn head_at(&self) -> f32 {
218 let TapeCursor { sector, cursor, .. } = self.tape_cursor;
219 sector as f32 + cursor as f32 / SECTOR_TS as f32
220 }
221 #[inline]
223 pub fn max_sectors(&self) -> usize {
224 self.sectors.len()
225 }
226 pub fn count_formatted(&self) -> usize {
228 self.sector_map.view_bits::<LocalBits>().count_ones()
229 }
230 pub fn iter_with_indices(&self) -> MicroCartridgeIdSecIter<'_> {
232 let iter_map = self.sector_map.view_bits::<LocalBits>().iter();
233 self.sectors.iter().zip(iter_map).enumerate().filter_map(&|(i, (s, valid))| {
234 if *valid { Some((i as u8, s)) } else { None }
235 })
236 }
237 #[inline]
239 pub fn is_write_protected(&self) -> bool {
240 self.protec
241 }
242 #[inline]
244 pub fn set_write_protected(&mut self, protect: bool) {
245 self.protec = protect;
246 }
247 #[inline]
252 pub fn is_sector_formatted(&self, sector: u8) -> bool {
253 self.sector_map.view_bits::<LocalBits>()[sector as usize]
254 }
255 pub fn new_with_sectors<S: Into<Vec<Sector>>>(
265 sectors: S,
266 write_protect: bool,
267 max_sectors: usize
268 ) -> Self
269 {
270 let mut sectors = sectors.into();
271 assert!(max_sectors > 0 && max_sectors <= MAX_SECTORS &&
272 sectors.len() <= max_sectors && sectors.len() <= MAX_USABLE_SECTORS);
273 let mut sector_map = [0u32;SECTOR_MAP_SIZE];
274 sector_map.view_bits_mut::<LocalBits>()[0..sectors.len()].fill(true);
275 sectors.resize(max_sectors, Sector::default());
276 let sectors = sectors.into_boxed_slice();
277 MicroCartridge {
278 sectors,
279 sector_map,
280 tape_cursor: TapeCursor::default(),
281 protec: write_protect,
282 written: None
283 }
284 }
285 pub fn new(max_sectors: usize) -> Self {
290 assert!(max_sectors > 0 && max_sectors <= MAX_SECTORS);
291 MicroCartridge {
292 sectors: vec![Sector::default();max_sectors].into_boxed_slice(),
293 sector_map: [0;SECTOR_MAP_SIZE],
294 tape_cursor: TapeCursor::default(),
295 protec: false,
296 written: None
297 }
298 }
299}
300
301impl<'a> IntoIterator for &'a MicroCartridge {
303 type Item = &'a Sector;
304 type IntoIter = MicroCartridgeSecIter<'a>;
305 #[inline]
306 fn into_iter(self) -> Self::IntoIter {
307 let iter_map = self.sector_map.view_bits::<LocalBits>().iter();
308 self.sectors.iter().zip(iter_map).filter_map(&|(s, valid)| {
309 if *valid { Some(s) } else { None }
310 })
311 }
312}
313
314impl<'a> IntoIterator for &'a mut MicroCartridge {
316 type Item = &'a mut Sector;
317 type IntoIter = MicroCartridgeSecIterMut<'a>;
318 #[inline]
319 fn into_iter(self) -> Self::IntoIter {
320 let iter_map = self.sector_map.view_bits::<LocalBits>().iter();
321 self.sectors.iter_mut().zip(iter_map).filter_map(&|(s, valid)| {
322 if *valid { Some(s) } else { None }
323 })
324 }
325}
326
327impl Index<u8> for MicroCartridge {
328 type Output = Sector;
329 #[inline]
330 fn index(&self, index: u8) -> &Self::Output {
331 &self.sectors[index as usize]
332 }
333}
334
335impl IndexMut<u8> for MicroCartridge {
336 #[inline]
337 fn index_mut(&mut self, index: u8) -> &mut Self::Output {
338 &mut self.sectors[index as usize]
339 }
340}
341
342const HEAD_PREAMBLE: u32 = 0;
343const HEAD_SYN_END: u32 = HEAD_START_TS - 1;
344const HEAD_START: u32 = HEAD_START_TS;
345const HEAD_END: u32 = HEAD_TS - 1;
346const GAP1_START: u32 = HEAD_TS;
347const GAP1_END: u32 = HEAD_TS + GAP1_TS - 1;
348const DATA_PREAMBLE: u32 = HEAD_TS + GAP1_TS;
349const DATA_SYN_END: u32 = DATA_PREAMBLE + DATA_START_TS - 1;
350const DATA_START: u32 = DATA_PREAMBLE + DATA_START_TS;
351const DATA_END: u32 = HEAD_TS + GAP1_TS + DATA_TS - 1;
352const GAP2_START: u32 = HEAD_TS + GAP1_TS + DATA_TS;
353
354impl SecPosition {
355 #[inline]
356 fn from_sector_cursor(cursor: u32) -> Self {
357 assert_eq!((HEAD_END + 1 - HEAD_START) / HEAD_SIZE as u32, BYTE_TS);
358 assert_eq!((DATA_END + 1 - DATA_START) / DATA_SIZE as u32, BYTE_TS);
359 match cursor {
360 HEAD_PREAMBLE..=HEAD_SYN_END => SecPosition::Preamble1(((cursor - HEAD_PREAMBLE)/BYTE_TS) as u16),
361 HEAD_START..=HEAD_END => SecPosition::Header(((cursor - HEAD_START)/BYTE_TS) as u16),
362 GAP1_START..=GAP1_END => SecPosition::Gap1,
363 DATA_PREAMBLE..=DATA_SYN_END => SecPosition::Preamble2(((cursor - DATA_PREAMBLE)/BYTE_TS) as u16),
364 DATA_START..=DATA_END => SecPosition::Data(((cursor - DATA_START)/BYTE_TS) as u16),
365 GAP2_START..=core::u32::MAX => SecPosition::Gap2,
366 }
367 }
368}
369
370impl TapeCursor {
371 #[inline]
372 fn forward(&mut self, ts: u32, seclen: u32) { let mut cursor = self.cursor.saturating_add(ts);
374 if cursor >= SECTOR_TS {
375 let diffsec = cursor / SECTOR_TS;
376 self.sector = Self::add_sectors(self.sector, diffsec, seclen);
377 cursor %= SECTOR_TS;
378 }
379 self.cursor = cursor;
380 self.secpos = SecPosition::from_sector_cursor(cursor);
381 }
382
383 #[inline]
384 fn add_sectors(sector: u8, delta: u32, seclen: u32) -> u8 {
385 ((sector as u32 + delta) % seclen) as u8
386 }
387}
388
389impl MicroCartridge {
390 #[inline(always)]
391 fn set_valid_sector(&mut self, sector: u8, valid: bool) {
392 self.sector_map.view_bits_mut::<LocalBits>().set(sector as usize, valid);
393 }
394
395 #[inline(always)]
396 fn clear_all_sectors(&mut self) {
397 for p in self.sector_map.iter_mut() {
398 *p = 0
399 }
400 }
401
402 fn erase_start(&mut self, delta_ts: u32) {
404 self.forward(delta_ts);
405 let TapeCursor { sector, secpos, .. } = self.tape_cursor;
406 self.written = None;
407 if self.is_sector_formatted(sector) {
408 match secpos {
409 SecPosition::Gap2 |
410 SecPosition::Gap1 => {} _ => { self.set_valid_sector(sector, false);
413 }
414 }
415 }
416 }
417
418 fn erase_forward(&mut self, delta_ts: u32) {
420 let prev_cursor = self.tape_cursor;
421 self.forward(delta_ts);
422 let TapeCursor { sector, secpos, .. } = self.tape_cursor;
423 if prev_cursor.sector != sector { if delta_ts >= SECTOR_TS * (self.sectors.len() as u32 - 1) {
425 self.clear_all_sectors();
426 }
427 else {
428 let prev_sector = if let SecPosition::Gap2 = prev_cursor.secpos {
429 TapeCursor::add_sectors(prev_cursor.sector, 1, self.sectors.len() as u32)
430 }
431 else {
432 prev_cursor.sector
433 };
434 if sector < prev_sector {
435 self.sector_map.view_bits_mut::<LocalBits>()[prev_sector.into()..].fill(false);
436 self.sector_map.view_bits_mut::<LocalBits>()[..sector.into()].fill(false);
437 }
438 else {
439 self.sector_map.view_bits_mut::<LocalBits>()[prev_sector.into()..=sector.into()].fill(false);
440 }
441 }
442 }
443 else {
444 match (prev_cursor.secpos, secpos) {
445 (SecPosition::Gap2, SecPosition::Gap2)|
447 (SecPosition::Gap1, SecPosition::Gap1)|
449 (SecPosition::Gap1, SecPosition::Preamble2(..))|
450 (SecPosition::Preamble2(..), SecPosition::Preamble2(..)) => {}
451 _ => { self.set_valid_sector(sector, false);
453 }
454 }
455 }
456 }
457
458 fn write_end(&mut self, delta_ts: u32) { self.forward(delta_ts);
460 if let Some(written) = self.written {
462 if delta_ts < 2*BYTE_TS {
463 const HEAD_SIZE_MIN: u16 = PREAMBLE_SIZE + HEAD_SIZE as u16;
464 const HEAD_SIZE_MAX: u16 = HEAD_SIZE_MIN + 55;
465 #[allow(clippy::single_match)]
468 match (written.get(), self.tape_cursor.secpos) {
469 (HEAD_SIZE_MIN..=HEAD_SIZE_MAX, SecPosition::Gap1) => {
470 self.set_valid_sector(self.tape_cursor.sector, true);
472 }
473 _ => {}
477 }
478 }
479 }
480 self.written = None;
481 }
482
483 fn write_data_forward(&mut self, data: u8, delta_ts: u32) -> u16 {
484 if let Some(written) = self.written {
485 self.written = NonZeroU16::new(written.get().saturating_add(1));
486 self.forward(delta_ts);
487 let TapeCursor { sector, cursor, secpos } = self.tape_cursor;
488 if delta_ts < BYTE_TS*3/2 {
489 match (written.get(), data, secpos) {
491 (wr, 0x00, SecPosition::Preamble1(offs @ 1..=9))|
492 (wr, 0xff, SecPosition::Preamble1(offs @ 10..=11)) if wr == offs => {
493 return (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16;
494 }
495 (wr, _, SecPosition::Preamble1(PREAMBLE_SIZE)) if wr == PREAMBLE_SIZE => {
496 self.sectors[sector as usize].head[0] = data;
497 return (HEAD_START + BYTE_TS - cursor) as u16;
498 }
499 (wr, _, SecPosition::Header(offset)) if wr == PREAMBLE_SIZE + offset => {
500 self.sectors[sector as usize].head[offset as usize] = data;
501 return (BYTE_TS - (cursor - HEAD_START) % BYTE_TS) as u16;
502 }
503 (wr, _, SecPosition::Gap1) if wr >= PREAMBLE_SIZE + HEAD_SIZE as u16 => {
504 return (BYTE_TS - (cursor - GAP1_START) % BYTE_TS) as u16;
505 }
506 (wr, 0x00, SecPosition::Preamble2(offs @ 1..=9))|
507 (wr, 0xff, SecPosition::Preamble2(offs @ 10..=11)) if wr == offs => {
508 return (BYTE_TS - (cursor - DATA_PREAMBLE) % BYTE_TS) as u16;
509 }
510 (wr, _, SecPosition::Preamble2(PREAMBLE_SIZE)) if wr == PREAMBLE_SIZE => {
511 self.sectors[sector as usize].data[0] = data;
512 return (DATA_START + BYTE_TS - cursor) as u16;
513 }
514 (wr, _, SecPosition::Data(offset)) if wr == PREAMBLE_SIZE + offset => {
515 self.sectors[sector as usize].data[offset as usize] = data;
516 return (BYTE_TS - (cursor - DATA_START) % BYTE_TS) as u16;
517 }
518 (wr, _, SecPosition::Gap2) if wr >= PREAMBLE_SIZE + DATA_SIZE as u16 => {
519 return (BYTE_TS - (cursor - GAP2_START) % BYTE_TS) as u16;
520 }
521 _=> {}
522 }
523 }
524 self.set_valid_sector(sector, false); (BYTE_TS - cursor % BYTE_TS) as u16
526 }
527 else {
528 self.erase_forward(delta_ts);
530 self.written = NonZeroU16::new(1);
531 let TapeCursor { mut sector, secpos, .. } = self.tape_cursor;
532 if data == 0 {
534 if self.is_sector_formatted(sector) {
535 if let SecPosition::Preamble2(0..=2)|SecPosition::Gap1 = secpos { self.tape_cursor.cursor = DATA_PREAMBLE;
538 self.tape_cursor.secpos = SecPosition::Preamble2(0);
539 return BYTE_TS as u16;
540 }
541 }
542 if let SecPosition::Gap2 = secpos { sector = TapeCursor::add_sectors(sector, 1, self.sectors.len() as u32);
544 }
545 self.tape_cursor.sector = sector;
548 self.tape_cursor.cursor = 0;
549 self.tape_cursor.secpos = SecPosition::Preamble1(0);
550 }
551 self.set_valid_sector(sector, false); BYTE_TS as u16
553 }
554 }
555
556 fn read_data_forward(&mut self, delta_ts: u32) -> (u8, u16) { self.forward(delta_ts);
558 let TapeCursor { sector, cursor, secpos, .. } = self.tape_cursor;
559 if self.is_sector_formatted(sector) {
560 return match secpos {
562 SecPosition::Preamble1(10..=11) => {
563 let data = self.sectors[sector as usize].head[0];
564 let delay = HEAD_START + BYTE_TS - cursor;
565 (data, delay as u16)
566 }
567 SecPosition::Header(offset) => {
568 let data = self.sectors[sector as usize].head[offset as usize];
569 let delay = BYTE_TS - (cursor - HEAD_START) % BYTE_TS;
570 (data, delay as u16)
571 }
572 SecPosition::Preamble1(..) => {
573 (0, (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16)
574 }
575 SecPosition::Gap1 => {
576 (!0, (BYTE_TS - (cursor - GAP1_START) % BYTE_TS) as u16)
577 }
578 SecPosition::Preamble2(10..=11) => {
579 let data = self.sectors[sector as usize].data[0];
580 let delay = DATA_START + BYTE_TS - cursor;
581 (data, delay as u16)
582 }
583 SecPosition::Data(offset) => {
584 let data = self.sectors[sector as usize].data[offset as usize];
585 let delay = BYTE_TS - (cursor - DATA_START) % BYTE_TS;
586 (data, delay as u16)
587 }
588 SecPosition::Preamble2(..) => {
589 (0, (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16)
590 }
591 SecPosition::Gap2 => {
592 let data = self.sectors[sector as usize].data[DATA_SIZE - 1];
593 (data, (BYTE_TS - (cursor - GAP2_START) % BYTE_TS) as u16)
594 }
595 }
596 }
597 (!0, (BYTE_TS - cursor % BYTE_TS) as u16)
598 }
599
600 #[inline(always)]
601 fn forward(&mut self, delta_ts: u32) {
602 self.tape_cursor.forward(delta_ts, self.sectors.len() as u32);
603 }
604
605 #[inline]
606 fn gap_syn_protect(&self) -> CartridgeState {
607 let mut gap = false;
608 let mut syn = false;
609 let TapeCursor { sector, secpos, .. } = self.tape_cursor;
610 if self.is_sector_formatted(sector) {
611 match secpos {
612 SecPosition::Preamble1(0..=9)|
613 SecPosition::Preamble2(0..=9) => {
614 gap = true;
615 }
616 SecPosition::Preamble1(10..=11)|
617 SecPosition::Preamble2(10..=11) => {
618 gap = true;
619 syn = true;
620 }
621 _ => {}
622 };
623 }
624 CartridgeState {
625 gap, syn, write_protect: self.protec
626 }
627 }
628}
629
630impl<T> ZxMicrodrives<T> {
631 pub fn replace_cartridge(
639 &mut self,
640 drive_index: usize,
641 cartridge: MicroCartridge
642 ) -> Option<MicroCartridge>
643 {
644 assert!(drive_index < MAX_DRIVES);
645 let prev_cartridge = self.drives[drive_index].replace(cartridge);
646 if self.erase {
647 if let Some(cartridge) = self.drive_if_current(drive_index) {
648 cartridge.erase_start(0);
649 }
650 }
651 prev_cartridge
652 }
653 pub fn take_cartridge(&mut self, index: usize) -> Option<MicroCartridge> {
660 assert!(index < MAX_DRIVES);
661 self.drives[index].take()
662 }
663 pub fn is_cartridge_inserted(&mut self, index: usize) -> bool {
670 assert!(index < MAX_DRIVES);
671 self.drives[index].is_some()
672 }
673 pub fn cartridge_at(&self, index: usize) -> Option<&MicroCartridge> {
680 assert!(index < MAX_DRIVES);
681 self.drives[index].as_ref()
682 }
683 pub fn cartridge_in_use(&self) -> Option<(usize, &MicroCartridge)> {
685 self.motor_on_drive.and_then(move |drive_on| {
686 let drive_index = (drive_on.get() - 1) as usize;
687 self.drives[drive_index & 7].as_ref()
688 .map(|mc| (drive_index, mc))
689 })
690 }
691
692 fn current_drive(&mut self) -> Option<&mut MicroCartridge> {
693 self.motor_on_drive.and_then(move |drive_on|
694 self.drives[(drive_on.get() - 1) as usize & 7].as_mut()
695 )
696 }
697
698 fn drive_if_current(&mut self, index: usize) -> Option<&mut MicroCartridge> {
699 self.motor_on_drive.and_then(move |drive_on| {
700 let drive_index = (drive_on.get() - 1) as usize;
701 if drive_index == index {
702 self.drives[drive_index & 7].as_mut()
703 }
704 else {
705 None
706 }
707 })
708 }
709}
710
711impl<T: TimestampOps> ZxMicrodrives<T> {
712 fn vts_diff_update(&mut self, timestamp: T) -> u32 {
713 let delta_ts = timestamp.diff_from(self.last_ts);
714 self.last_ts = timestamp;
715 debug_assert!(delta_ts >= 0);
716 delta_ts as u32
717 }
718
719 pub(crate) fn reset(&mut self, timestamp: T) {
720 let delta_ts = self.vts_diff_update(timestamp);
721 self.stop_motor(delta_ts);
722 self.write = false;
723 self.erase = false;
724 self.comms_clk = false;
725 }
726
727 pub(crate) fn update_timestamp(&mut self, timestamp: T) {
728 let delta_ts = self.vts_diff_update(timestamp);
729 let (erase, write) = (self.erase, self.write);
730 if let Some(cartridge) = self.current_drive() {
731 if erase && (!write || delta_ts > 2*BYTE_TS) {
732 cartridge.erase_forward(delta_ts);
733 }
734 else {
735 cartridge.forward(delta_ts);
736 }
737 }
738 }
739 pub(crate) fn next_frame(&mut self, eof_timestamp: T) {
741 self.last_ts = self.last_ts.saturating_sub(eof_timestamp);
742 }
743
744 pub(crate) fn read_state(&mut self, timestamp: T) -> CartridgeState {
745 let delta_ts = self.vts_diff_update(timestamp);
746 let erase = self.erase;
747 if let Some(cartridge) = self.current_drive() {
748 if erase {
749 cartridge.erase_forward(delta_ts);
750 }
751 else {
752 cartridge.forward(delta_ts);
753 }
754 return cartridge.gap_syn_protect();
755 }
756 CartridgeState::default()
757 }
758
759 pub(crate) fn write_control(
762 &mut self,
763 timestamp: T,
764 erase: bool,
765 write: bool,
766 comms_clk: bool,
767 comms_out: bool
768 )
769 {
770 let delta_ts = self.vts_diff_update(timestamp);
771 if comms_clk != self.comms_clk {
772 self.comms_clk = comms_clk;
773 if comms_clk { if comms_out { self.stop_motor(delta_ts);
776 self.motor_on_drive = NonZeroU8::new(1);
777 }
778 else { self.motor_on_drive = self.stop_motor(delta_ts).and_then(|n_drive|
780 if n_drive.get() == 8 {
781 None
782 } else {
783 NonZeroU8::new(n_drive.get() + 1)
784 }
785 );
786 }
787 self.erase = false;
789 self.write = false;
790 }
791 }
792
793 if erase && !self.erase {
794 if let Some(cartridge) = self.current_drive() {
795 cartridge.erase_start(delta_ts);
796 }
797 }
798 else if self.erase {
799 if !write && self.write {
800 if let Some(cartridge) = self.current_drive() {
801 cartridge.write_end(delta_ts);
802 }
803 }
804 else if (write && !self.write) || (!erase && self.erase) {
805 if let Some(cartridge) = self.current_drive() {
806 cartridge.erase_forward(delta_ts);
807 }
808 }
809 }
810 self.erase = erase;
811 self.write = write;
812 }
813
814 pub(crate) fn write_data(&mut self, data: u8, timestamp: T) -> u16 {
815 let delta_ts = self.vts_diff_update(timestamp);
816 if self.write && self.erase { if let Some(cartridge) = self.current_drive() {
818 return cartridge.write_data_forward(data, delta_ts);
819 }
820 }
821 0
822 }
823
824 pub(crate) fn read_data(&mut self, timestamp: T) -> (u8, Option<NonZeroU16>) {
825 let delta_ts = self.vts_diff_update(timestamp);
826 if self.erase {
827 if let Some(cartridge) = self.current_drive() {
828 cartridge.erase_forward(delta_ts);
829 }
830 }
831 if !(self.write || self.erase) {
832 if let Some(cartridge) = self.current_drive() {
833 let (data, delay) = cartridge.read_data_forward(delta_ts);
834 return (data, NonZeroU16::new(delay))
835 }
836 }
837 (!0, HALT_FOREVER_TS)
839 }
840
841 fn stop_motor(&mut self, delta_ts: u32) -> Option<NonZeroU8> {
842 let motor_on_drive = self.motor_on_drive;
843 if self.erase {
844 if let Some(cartridge) = self.current_drive() {
845 cartridge.erase_forward(delta_ts);
846 cartridge.written = None;
847 }
848 }
849 self.motor_on_drive = None;
850 motor_on_drive
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use core::num::Wrapping;
857 use spectrusty_core::clock::FTs;
858 use spectrusty_core::z80emu::host::TsCounter;
859 use super::*;
860 type UlaTsCounter = TsCounter<FTs>;
861 type TestMicrodrives = ZxMicrodrives<FTs>;
862
863 const EOF: FTs = 69888;
864
865 fn is_eof(tsc: TsCounter<FTs>) -> bool {
866 (*tsc).0 > EOF - 69
867 }
868
869 fn wrap_frame(tsc: &mut TsCounter<FTs>) {
870 while is_eof(*tsc) {
871 **tsc -= Wrapping(EOF);
872 }
873 }
874
875 #[test]
876 fn microdrives_works() {
877 let mut drive: TestMicrodrives = Default::default();
878 assert_eq!(drive.write, false);
879 assert_eq!(drive.erase, false);
880 assert_eq!(drive.comms_clk, false);
881 assert_eq!(drive.motor_on_drive, None);
882 assert_eq!(drive.read_data(10), (!0, NonZeroU16::new(65535)));
883 assert_eq!(drive.last_ts, 10);
884 assert_eq!(drive.write_data(0xAA, 11), 0);
885 assert_eq!(drive.last_ts, 11);
886 assert_eq!(drive.read_state(20), CartridgeState{ gap: false, syn: false, write_protect: false});
887 assert_eq!(drive.last_ts, 20);
888 drive.write_control(30, true, false, true, true);
889 assert_eq!(drive.write, false);
890 assert_eq!(drive.erase, true);
891 assert_eq!(drive.comms_clk, true);
892 assert_eq!(drive.last_ts, 30);
893 assert_eq!(drive.motor_on_drive, NonZeroU8::new(1));
894 drive.write_control(40, true, false, false, false);
895 assert_eq!(drive.write, false);
896 assert_eq!(drive.erase, true);
897 assert_eq!(drive.comms_clk, false);
898 assert_eq!(drive.last_ts, 40);
899 assert_eq!(drive.motor_on_drive, NonZeroU8::new(1));
900 assert_eq!(drive.replace_cartridge(1, MicroCartridge::new(5)).is_none(), true);
901 drive.write_control(40, true, false, true, false);
902 drive.write_control(50, true, true, false, false);
904 assert_eq!(drive.write, true);
905 assert_eq!(drive.erase, true);
906 assert_eq!(drive.comms_clk, false);
907 assert_eq!(drive.last_ts, 50);
908 assert_eq!(drive.motor_on_drive, NonZeroU8::new(2));
909 assert_eq!(drive.read_state(60), CartridgeState { gap: false, syn: false, write_protect: false});
910 let mut utsc = UlaTsCounter::from(224);
911 for _ in 0..10 {
912 let delay = drive.write_data(0, (*utsc).0);
913 println!("delay 0x00: {} {:?}", delay, utsc);
914 *utsc += Wrapping(delay as FTs + 21 + 16);
915 }
916 for _ in 0..2 {
917 let delay = drive.write_data(!0, (*utsc).0);
918 println!("delay 0xff: {} {:?}", delay, utsc);
919 *utsc += Wrapping(delay as FTs + 11 + 13);
920 }
921 for i in 1..19 {
922 let delay = drive.write_data(i, (*utsc).0);
923 println!("delay: 0x{:02x} {} {:?}", i, delay, utsc);
924 *utsc += Wrapping(delay as FTs + 21 + 16);
925 }
926 let cartridge = drive.current_drive().unwrap();
927 assert_eq!(cartridge.sector_map, [0x00000000, 0x00000000, 0x00000000, 0x00000000,
928 0x00000000, 0x00000000, 0x00000000, 0x00000000]);
929 assert_eq!(cartridge.written, NonZeroU16::new(30));
930 assert_eq!(cartridge.sectors[0].head, [1u8,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
931 assert_eq!(&cartridge.sectors[0].data[..], &[!0u8;528][..]);
932 assert_eq!(cartridge.tape_cursor, TapeCursor {
933 cursor: GAP1_START + 2 * BYTE_TS + 21 + 16,
934 sector: 0,
935 secpos: SecPosition::Gap1,
936 });
937 *utsc += Wrapping(36);
938 drive.write_control((*utsc).0, true, false, false, false);
940 let cartridge = drive.current_drive().unwrap();
941 assert_eq!(cartridge.written, None);
942 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
943 assert_eq!(sector_map[0], true);
944 for valid in §or_map[1..] {
945 assert_eq!(*valid, false);
946 }
947 assert_eq!(cartridge.sectors[0].head, [1u8,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
948 assert_eq!(&cartridge.sectors[0].data[..], &[!0u8;528][..]);
949 assert_eq!(cartridge.tape_cursor, TapeCursor {
950 cursor: GAP1_START + 3 * BYTE_TS + 21 + 16 + 36,
951 sector: 0,
952 secpos: SecPosition::Gap1,
953 });
954 *utsc += Wrapping(11443);
956 drive.write_control((*utsc).0, true, true, false, false);
958 *utsc += Wrapping(48);
959 for _ in 0..10 {
960 let delay = drive.write_data(0, (*utsc).0);
961 println!("delay 0x00: {} {:?}", delay, utsc);
962 *utsc += Wrapping(delay as FTs + 11 + 13);
963 }
964 for _ in 0..2 {
965 let delay = drive.write_data(!0, (*utsc).0);
966 println!("delay 0xff: {} {:?}", delay, utsc);
967 *utsc += Wrapping(delay as FTs + 19);
968 }
969 for i in 1..630u16 { if is_eof(utsc) {
971 drive.update_timestamp((*utsc).0);
972 drive.next_frame(EOF);
973 wrap_frame(&mut utsc);
974 }
975 let delay = drive.write_data(!(i as u8), (*utsc).0);
976 println!("delay: 0x{:02x} {} {:?}", i, delay, utsc);
977 *utsc += Wrapping(delay as FTs + 11 + 13);
978 }
979 let cartridge = drive.current_drive().unwrap();
980 assert_eq!(cartridge.written, NonZeroU16::new(641));
981 assert_eq!(cartridge.sectors[0].head, [1u8,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
982 for (i, data) in cartridge.sectors[0].data.iter().enumerate() {
983 assert_eq!(!(i + 1) as u8, *data);
984 }
985 assert_eq!(cartridge.tape_cursor, TapeCursor {
986 cursor: 121224, sector: 0, secpos: SecPosition::Gap2 });
987 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
988 assert_eq!(sector_map[0], true);
989 drive.write_control((*utsc).0, true, false, false, false);
991 let cartridge = drive.current_drive().unwrap();
992 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
993 assert_eq!(sector_map[0], true);
994 *utsc += Wrapping(8941);
996 drive.write_control((*utsc).0, false, false, false, false);
998 let cartridge = drive.current_drive().unwrap();
999 assert_eq!(cartridge.tape_cursor, TapeCursor {
1000 cursor: 827, sector: 1, secpos: SecPosition::Preamble1(5) });
1001 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
1002 assert_eq!(sector_map[0], true);
1003 for valid in §or_map[1..] {
1004 assert_eq!(*valid, false);
1005 }
1006
1007 fn find_gap_sync(mut utsc: UlaTsCounter, drive: &mut TestMicrodrives) -> UlaTsCounter {
1008 let mut counter = 0;
1009 while !drive.read_state((*utsc).0).gap {
1010 counter += 1;
1011 *utsc += Wrapping(108);
1012 if is_eof(utsc) {
1013 drive.update_timestamp((*utsc).0);
1014 drive.next_frame(EOF);
1015 wrap_frame(&mut utsc);
1016 }
1017 }
1018 println!("counter: {}", counter);
1019 for _ in 1..6 {
1020 *utsc += Wrapping(88);
1021 assert!(drive.read_state((*utsc).0).gap);
1022 }
1023 *utsc += Wrapping(25);
1024 'outer: loop {
1025 for i in 1..=60 {
1026 let state = drive.read_state((*utsc).0);
1027 *utsc += Wrapping(38);
1028 assert!(state.gap);
1029 if state.syn { break 'outer }
1030 println!("sync not found: {}", i);
1031 }
1032 assert!(false, "failed to find syn");
1033 }
1034 utsc
1035 }
1036
1037 *utsc += Wrapping(108);
1039 utsc = find_gap_sync(utsc, &mut drive);
1040 let cartridge = drive.current_drive().unwrap();
1041 println!("{:?} {:?}", cartridge.tape_cursor, utsc);
1042 *utsc += Wrapping(92);
1043 for i in 1..=15 {
1044 let (data, delay) = drive.read_data((*utsc).0);
1045 println!("data: {:02x} delay: {:?} {:?}", data, delay, utsc);
1046 assert_eq!(i, data);
1047 *utsc += Wrapping(delay.unwrap().get() as FTs + 21 + 16);
1048 }
1049 let (data, delay) = drive.read_data((*utsc).0);
1050 println!("data: {:02x} delay: {:?} {:?}", data, delay, utsc);
1051 assert_eq!(data, !0);
1052 *utsc += Wrapping(delay.unwrap().get() as FTs + 121);
1053
1054 assert_eq!(drive.read_state((*utsc).0).gap, false);
1056 *utsc += Wrapping(10000);
1057 assert_eq!(drive.read_state((*utsc).0).gap, false);
1058 utsc = find_gap_sync(utsc, &mut drive);
1059 let cartridge = drive.current_drive().unwrap();
1060 println!("{:?} {:?}", cartridge.tape_cursor, utsc);
1061 *utsc += Wrapping(92);
1062 for i in 1..=528 {
1063 let (data, delay) = drive.read_data((*utsc).0);
1064 println!("data: {:02x} delay: {:?} {:?}", data, delay, utsc);
1065 assert_eq!(!i as u8, data);
1066 *utsc += Wrapping(delay.unwrap().get() as FTs + 21 + 16);
1067 if is_eof(utsc) {
1068 drive.update_timestamp((*utsc).0);
1069 drive.next_frame(EOF);
1070 wrap_frame(&mut utsc);
1071 }
1072 }
1073 let (data, delay) = drive.read_data((*utsc).0);
1074 println!("data: {:02x} delay: {:?} {:?}", data, delay, utsc);
1075 assert_eq!(data, 239);
1076 *utsc += Wrapping(delay.unwrap().get() as FTs + 21);
1077
1078 *utsc += Wrapping(10800);
1080 utsc = find_gap_sync(utsc, &mut drive);
1081 let cartridge = drive.current_drive().unwrap();
1082 println!("{:?} {:?}", cartridge.tape_cursor, utsc);
1083 *utsc += Wrapping(92);
1084 for i in 1..=15 {
1085 let (data, delay) = drive.read_data((*utsc).0);
1086 println!("data: {:02x} delay: {:?} {:?}", data, delay, utsc);
1087 assert_eq!(i, data);
1088 *utsc += Wrapping(delay.unwrap().get() as FTs + 21 + 16);
1089 }
1090 assert_eq!(drive.read_state((*utsc).0), CartridgeState {
1092 gap: false, syn: false, write_protect: false});
1093 *utsc += Wrapping(98);
1094 drive.write_control((*utsc).0, true, false, false, false);
1095 *utsc += Wrapping(9524);
1096 drive.write_control((*utsc).0, true, true, false, false);
1097 *utsc += Wrapping(47);
1099 for _ in 0..10 {
1100 let delay = drive.write_data(0, (*utsc).0);
1101 println!("delay 0x00: {} {:?}", delay, utsc);
1102 *utsc += Wrapping(delay as FTs + 21);
1103 }
1104 for _ in 0..2 {
1105 let delay = drive.write_data(!0, (*utsc).0);
1106 println!("delay 0xff: {} {:?}", delay, utsc);
1107 *utsc += Wrapping(delay as FTs + 21);
1108 }
1109 for i in 0..531u16 { if is_eof(utsc) {
1111 drive.update_timestamp((*utsc).0);
1112 drive.next_frame(EOF);
1113 wrap_frame(&mut utsc);
1114 }
1115 let delay = drive.write_data(0xA5^(i as u8), (*utsc).0);
1116 println!("delay: 0x{:02x} {} {:?}", i, delay, utsc);
1117 *utsc += Wrapping(delay as FTs + 21 + 16);
1118 }
1119 let cartridge = drive.current_drive().unwrap();
1120 assert_eq!(cartridge.written, NonZeroU16::new(543));
1121 assert_eq!(cartridge.sectors[0].head, [1u8,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
1122 for (i, data) in cartridge.sectors[0].data.iter().enumerate() {
1123 assert_eq!(0xA5^(i as u8), *data);
1124 }
1125 assert_eq!(cartridge.tape_cursor, TapeCursor {
1126 cursor: 105361, sector: 0, secpos: SecPosition::Gap2 });
1127 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
1128 assert_eq!(sector_map[0], true);
1129 *utsc += Wrapping(18);
1131 drive.write_control((*utsc).0, true, false, false, false);
1132 *utsc += Wrapping(112);
1134 drive.write_control((*utsc).0, false, false, false, false);
1135 let cartridge = drive.current_drive().unwrap();
1136 assert_eq!(cartridge.tape_cursor, TapeCursor {
1137 cursor: 105653, sector: 0, secpos: SecPosition::Gap2 });
1138 let sector_map = cartridge.sector_map.view_bits::<LocalBits>();
1139 assert_eq!(sector_map[0], true);
1140 for valid in §or_map[1..] {
1141 assert_eq!(*valid, false);
1142 }
1143 *utsc += Wrapping(1000);
1145 if is_eof(utsc) {
1146 drive.update_timestamp((*utsc).0);
1147 drive.next_frame(EOF);
1148 wrap_frame(&mut utsc);
1149 }
1150 for i in 0..7 {
1151 assert_eq!(drive.motor_on_drive, NonZeroU8::new(i + 2));
1152 drive.write_control((*utsc).0, true, false, true, false);
1153 *utsc += Wrapping(3500);
1154 drive.write_control((*utsc).0, true, false, false, false);
1155 *utsc += Wrapping(3500);
1156 }
1157 assert_eq!(drive.motor_on_drive, None);
1158 }
1159}