Struct spectrusty_peripherals::storage::microdrives::MicroCartridge
source · pub struct MicroCartridge { /* private fields */ }
Expand description
This struct represents an emulated Microdrive tape cartridge.
It consist of up to MAX_SECTORS Sectors. Instances of this struct can be “inserted” into one of 8 ZxMicrodrives’s emulator drives.
Implementations§
source§impl MicroCartridge
impl MicroCartridge
sourcepub fn head_at(&self) -> f32
pub fn head_at(&self) -> f32
Returns the current drive’s head position counted in sectors as floating point value.
The fractional part indicates how far the head position is within a sector.
sourcepub fn max_sectors(&self) -> usize
pub fn max_sectors(&self) -> usize
Returns the number of the emulated physical sectors on the tape.
sourcepub fn count_formatted(&self) -> usize
pub fn count_formatted(&self) -> usize
Returns the number of formatted sectors.
sourcepub fn iter_with_indices(&self) -> MicroCartridgeIdSecIter<'_>
pub fn iter_with_indices(&self) -> MicroCartridgeIdSecIter<'_>
Returns an iterator of formatted sectors with their original indices.
sourcepub fn is_write_protected(&self) -> bool
pub fn is_write_protected(&self) -> bool
Returns true
if the cartridge is write protected.
sourcepub fn set_write_protected(&mut self, protect: bool)
pub fn set_write_protected(&mut self, protect: bool)
Changes the write protected flag of the cartridge.
sourcepub fn is_sector_formatted(&self, sector: u8) -> bool
pub fn is_sector_formatted(&self, sector: u8) -> bool
Returns true
if the given sector
is formatted.
Panics
Panics if sector
equals to or is above the max_sectors
limit.
Examples found in repository?
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
fn erase_start(&mut self, delta_ts: u32) {
self.forward(delta_ts);
let TapeCursor { sector, secpos, .. } = self.tape_cursor;
self.written = None;
if self.is_sector_formatted(sector) {
match secpos {
SecPosition::Gap2 |
SecPosition::Gap1 => {} // synchronized sector write may follow
_ => { // otherwise clear sector
self.set_valid_sector(sector, false);
}
}
}
}
// called when writing began or just erasing ended / motor stopped etc
fn erase_forward(&mut self, delta_ts: u32) {
let prev_cursor = self.tape_cursor;
self.forward(delta_ts);
let TapeCursor { sector, secpos, .. } = self.tape_cursor;
if prev_cursor.sector != sector { // clear all previous sectors
if delta_ts >= SECTOR_TS * (self.sectors.len() as u32 - 1) {
self.clear_all_sectors();
}
else {
let prev_sector = if let SecPosition::Gap2 = prev_cursor.secpos {
TapeCursor::add_sectors(prev_cursor.sector, 1, self.sectors.len() as u32)
}
else {
prev_cursor.sector
};
if sector < prev_sector {
self.sector_map.view_bits_mut::<LocalBits>()[prev_sector.into()..].fill(false);
self.sector_map.view_bits_mut::<LocalBits>()[..sector.into()].fill(false);
}
else {
self.sector_map.view_bits_mut::<LocalBits>()[prev_sector.into()..=sector.into()].fill(false);
}
}
}
else {
match (prev_cursor.secpos, secpos) {
// synchronized write has ended
(SecPosition::Gap2, SecPosition::Gap2)|
// synchronized write should follow
(SecPosition::Gap1, SecPosition::Gap1)|
(SecPosition::Gap1, SecPosition::Preamble2(..))|
(SecPosition::Preamble2(..), SecPosition::Preamble2(..)) => {}
_ => { // otherwise clear sector
self.set_valid_sector(sector, false);
}
}
}
}
fn write_end(&mut self, delta_ts: u32) { // switched r/w w -> r, erase off, or motor off
self.forward(delta_ts);
// println!("wr: {:?}, delta: {} sec: {} cur: {} {:?}", self.written, delta_ts, self.tape_cursor.sector, self.tape_cursor.cursor, self.tape_cursor.secpos);
if let Some(written) = self.written {
if delta_ts < 2*BYTE_TS {
const HEAD_SIZE_MIN: u16 = PREAMBLE_SIZE + HEAD_SIZE as u16;
const HEAD_SIZE_MAX: u16 = HEAD_SIZE_MIN + 55;
// const DATA_SIZE_MIN: u16 = PREAMBLE_SIZE + DATA_SIZE as u16;
// const DATA_SIZE_MAX: u16 = DATA_SIZE_MIN + 110;
#[allow(clippy::single_match)]
match (written.get(), self.tape_cursor.secpos) {
(HEAD_SIZE_MIN..=HEAD_SIZE_MAX, SecPosition::Gap1) => {
// this may yield a "valid" sector with invalid data, but harmless
self.set_valid_sector(self.tape_cursor.sector, true);
}
// (DATA_SIZE_MIN..=DATA_SIZE_MAX, SecPosition::Gap2) => {
// println!("ok valid data");
// }
_ => {}
}
}
}
self.written = None;
}
fn write_data_forward(&mut self, data: u8, delta_ts: u32) -> u16 {
if let Some(written) = self.written {
self.written = NonZeroU16::new(written.get().saturating_add(1));
self.forward(delta_ts);
let TapeCursor { sector, cursor, secpos } = self.tape_cursor;
if delta_ts < BYTE_TS*3/2 {
// println!("wr: {}, data: {:x}, sec: {} cur: {} {:?}", written.get(), data, sector, cursor, secpos);
match (written.get(), data, secpos) {
(wr, 0x00, SecPosition::Preamble1(offs @ 1..=9))|
(wr, 0xff, SecPosition::Preamble1(offs @ 10..=11)) if wr == offs => {
return (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16;
}
(wr, _, SecPosition::Preamble1(PREAMBLE_SIZE)) if wr == PREAMBLE_SIZE => {
self.sectors[sector as usize].head[0] = data;
return (HEAD_START + BYTE_TS - cursor) as u16;
}
(wr, _, SecPosition::Header(offset)) if wr == PREAMBLE_SIZE + offset => {
self.sectors[sector as usize].head[offset as usize] = data;
return (BYTE_TS - (cursor - HEAD_START) % BYTE_TS) as u16;
}
(wr, _, SecPosition::Gap1) if wr >= PREAMBLE_SIZE + HEAD_SIZE as u16 => {
return (BYTE_TS - (cursor - GAP1_START) % BYTE_TS) as u16;
}
(wr, 0x00, SecPosition::Preamble2(offs @ 1..=9))|
(wr, 0xff, SecPosition::Preamble2(offs @ 10..=11)) if wr == offs => {
return (BYTE_TS - (cursor - DATA_PREAMBLE) % BYTE_TS) as u16;
}
(wr, _, SecPosition::Preamble2(PREAMBLE_SIZE)) if wr == PREAMBLE_SIZE => {
self.sectors[sector as usize].data[0] = data;
return (DATA_START + BYTE_TS - cursor) as u16;
}
(wr, _, SecPosition::Data(offset)) if wr == PREAMBLE_SIZE + offset => {
self.sectors[sector as usize].data[offset as usize] = data;
return (BYTE_TS - (cursor - DATA_START) % BYTE_TS) as u16;
}
(wr, _, SecPosition::Gap2) if wr >= PREAMBLE_SIZE + DATA_SIZE as u16 => {
return (BYTE_TS - (cursor - GAP2_START) % BYTE_TS) as u16;
}
_=> {}
}
}
self.set_valid_sector(sector, false); // just erase all sector
(BYTE_TS - cursor % BYTE_TS) as u16
}
else {
// println!("start: {}", delta_ts);
self.erase_forward(delta_ts);
self.written = NonZeroU16::new(1);
let TapeCursor { mut sector, secpos, .. } = self.tape_cursor;
// println!("sector: {} cur: {} wr: {:?}, data: {:x}, {:?}", sector, self.tape_cursor.cursor, self.written, data, secpos);
if data == 0 {
if self.is_sector_formatted(sector) {
// println!("overwrite data block");
if let SecPosition::Preamble2(0..=2)|SecPosition::Gap1 = secpos { // synchronized write data sector
self.tape_cursor.cursor = DATA_PREAMBLE;
self.tape_cursor.secpos = SecPosition::Preamble2(0);
return BYTE_TS as u16;
}
}
if let SecPosition::Gap2 = secpos { // write starts on next sector
sector = TapeCursor::add_sectors(sector, 1, self.sectors.len() as u32);
}
// println!("begin sector");
// write start somewhere in the middle of the sector, we just make it the new beginning of a sector
self.tape_cursor.sector = sector;
self.tape_cursor.cursor = 0;
self.tape_cursor.secpos = SecPosition::Preamble1(0);
}
self.set_valid_sector(sector, false); // just erase this sector
BYTE_TS as u16
}
}
fn read_data_forward(&mut self, delta_ts: u32) -> (u8, u16) { // data, delay
self.forward(delta_ts);
let TapeCursor { sector, cursor, secpos, .. } = self.tape_cursor;
if self.is_sector_formatted(sector) {
// println!("sec: {} cur: {} {:?} {:?}", sector, cursor, secpos, res);
return match secpos {
SecPosition::Preamble1(10..=11) => {
let data = self.sectors[sector as usize].head[0];
let delay = HEAD_START + BYTE_TS - cursor;
(data, delay as u16)
}
SecPosition::Header(offset) => {
let data = self.sectors[sector as usize].head[offset as usize];
let delay = BYTE_TS - (cursor - HEAD_START) % BYTE_TS;
(data, delay as u16)
}
SecPosition::Preamble1(..) => {
(0, (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16)
}
SecPosition::Gap1 => {
(!0, (BYTE_TS - (cursor - GAP1_START) % BYTE_TS) as u16)
}
SecPosition::Preamble2(10..=11) => {
let data = self.sectors[sector as usize].data[0];
let delay = DATA_START + BYTE_TS - cursor;
(data, delay as u16)
}
SecPosition::Data(offset) => {
let data = self.sectors[sector as usize].data[offset as usize];
let delay = BYTE_TS - (cursor - DATA_START) % BYTE_TS;
(data, delay as u16)
}
SecPosition::Preamble2(..) => {
(0, (BYTE_TS - (cursor - HEAD_PREAMBLE) % BYTE_TS) as u16)
}
SecPosition::Gap2 => {
let data = self.sectors[sector as usize].data[DATA_SIZE - 1];
(data, (BYTE_TS - (cursor - GAP2_START) % BYTE_TS) as u16)
}
}
}
(!0, (BYTE_TS - cursor % BYTE_TS) as u16)
}
#[inline(always)]
fn forward(&mut self, delta_ts: u32) {
self.tape_cursor.forward(delta_ts, self.sectors.len() as u32);
}
#[inline]
fn gap_syn_protect(&self) -> CartridgeState {
let mut gap = false;
let mut syn = false;
let TapeCursor { sector, secpos, .. } = self.tape_cursor;
if self.is_sector_formatted(sector) {
match secpos {
SecPosition::Preamble1(0..=9)|
SecPosition::Preamble2(0..=9) => {
gap = true;
}
SecPosition::Preamble1(10..=11)|
SecPosition::Preamble2(10..=11) => {
gap = true;
syn = true;
}
_ => {}
};
}
CartridgeState {
gap, syn, write_protect: self.protec
}
}
sourcepub fn new_with_sectors<S: Into<Vec<Sector>>>(
sectors: S,
write_protect: bool,
max_sectors: usize
) -> Self
pub fn new_with_sectors<S: Into<Vec<Sector>>>(
sectors: S,
write_protect: bool,
max_sectors: usize
) -> Self
Creates a new instance of MicroCartridge with provided sectors.
Note
Content of the sectors is not verified and is assumed to be properly formatted.
Panics
The number of sectors provided must not be greater than MAX_USABLE_SECTORS.
max_sectors
must not be 0 and must be grater or equal to the number of provided sectors and
must not be greater than MAX_SECTORS.
sourcepub fn new(max_sectors: usize) -> Self
pub fn new(max_sectors: usize) -> Self
Creates a new instance of MicroCartridge with custom max_sectors
number.
Panics
max_sectors
must not be 0 and must not be greater than MAX_SECTORS.
Trait Implementations§
source§impl Clone for MicroCartridge
impl Clone for MicroCartridge
source§fn clone(&self) -> MicroCartridge
fn clone(&self) -> MicroCartridge
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moresource§impl Debug for MicroCartridge
impl Debug for MicroCartridge
source§impl Default for MicroCartridge
impl Default for MicroCartridge
source§impl<'de> Deserialize<'de> for MicroCartridge
impl<'de> Deserialize<'de> for MicroCartridge
source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
source§impl Index<u8> for MicroCartridge
impl Index<u8> for MicroCartridge
source§impl IndexMut<u8> for MicroCartridge
impl IndexMut<u8> for MicroCartridge
source§impl<'a> IntoIterator for &'a MicroCartridge
impl<'a> IntoIterator for &'a MicroCartridge
Iterates through formatted sectors.
source§impl<'a> IntoIterator for &'a mut MicroCartridge
impl<'a> IntoIterator for &'a mut MicroCartridge
Iterates through formatted sectors.
Auto Trait Implementations§
impl RefUnwindSafe for MicroCartridge
impl Send for MicroCartridge
impl Sync for MicroCartridge
impl Unpin for MicroCartridge
impl UnwindSafe for MicroCartridge
Blanket Implementations§
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self
to use its Binary
implementation when Debug
-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self
to use its Octal
implementation when Debug
-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
source§impl<S, T> IntoSample<S> for Twhere
S: FromSample<T>,
impl<S, T> IntoSample<S> for Twhere
S: FromSample<T>,
source§fn into_sample(self) -> S
fn into_sample(self) -> S
S
a sample type from self
.§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> Rwhere
Self: Borrow<B>,
B: 'a + ?Sized,
R: 'a,
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> Rwhere
Self: Borrow<B>,
B: 'a + ?Sized,
R: 'a,
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R
) -> Rwhere
Self: BorrowMut<B>,
B: 'a + ?Sized,
R: 'a,
fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R
) -> Rwhere
Self: BorrowMut<B>,
B: 'a + ?Sized,
R: 'a,
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> Rwhere
Self: AsRef<U>,
U: 'a + ?Sized,
R: 'a,
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> Rwhere
Self: AsRef<U>,
U: 'a + ?Sized,
R: 'a,
self
, then passes self.as_ref()
into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> Rwhere
Self: AsMut<U>,
U: 'a + ?Sized,
R: 'a,
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> Rwhere
Self: AsMut<U>,
U: 'a + ?Sized,
R: 'a,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
Borrow<B>
of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
BorrowMut<B>
of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
AsRef<R>
view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
AsMut<R>
view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Selfwhere
Self: Deref<Target = T>,
T: ?Sized,
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Selfwhere
Self: Deref<Target = T>,
T: ?Sized,
Deref::Target
of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Selfwhere
Self: DerefMut<Target = T> + Deref,
T: ?Sized,
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Selfwhere
Self: DerefMut<Target = T> + Deref,
T: ?Sized,
Deref::Target
of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap()
only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut()
only in debug builds, and is erased in release
builds. Read more§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Selfwhere
Self: Borrow<B>,
B: ?Sized,
.tap_borrow()
only in debug builds, and is erased in release
builds. Read more§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Selfwhere
Self: BorrowMut<B>,
B: ?Sized,
.tap_borrow_mut()
only in debug builds, and is erased in release
builds. Read more§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Selfwhere
Self: AsRef<R>,
R: ?Sized,
.tap_ref()
only in debug builds, and is erased in release
builds. Read more§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Selfwhere
Self: AsMut<R>,
R: ?Sized,
.tap_ref_mut()
only in debug builds, and is erased in release
builds. Read more