use core::fmt::Debug;
use core::ops::{Deref, DerefMut};
#[cfg(feature = "snapshot")]
use serde::{Serialize, Deserialize};
use spectrusty_core::clock::*;
pub const DOTS_PER_LINE: u32 = 256;
pub const BYTES_PER_LINE: u32 = 32;
pub trait Spooler: Debug + Default {
fn motor_on(&mut self) {}
fn motor_off(&mut self) {}
fn push_line(&mut self, line: &[u8]);
}
#[derive(Clone, Copy, Default, Debug)]
#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
pub struct DebugSpooler;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "snapshot", serde(rename_all="camelCase"))]
pub struct ZxPrinterDevice<T, S> {
#[cfg_attr(feature = "snapshot", serde(skip))]
pub spooler: S,
pub bit_delay: u16,
motor: bool,
ready: bool,
cursor: u8,
ready_ts: T,
line: [u8;32]
}
const STATUS_OFF: u8 = 0b0011_1110;
const STATUS_NEW_LINE: u8 = 0b1011_1111;
const STATUS_BIT_READY: u8 = 0b0011_1111;
const STATUS_NOT_READY: u8 = 0b0011_1110;
const STYLUS_MASK: u8 = 0b1000_0000;
const MOTOR_MASK: u8 = 0b0000_0100;
const SLOW_MASK: u8 = 0b0000_0010;
const BIT_DELAY: u16 = 855;
impl<T: Default, S: Default> Default for ZxPrinterDevice<T, S> {
fn default() -> Self {
ZxPrinterDevice {
spooler: S::default(),
bit_delay: BIT_DELAY,
motor: false,
ready: false,
cursor: 0,
ready_ts: T::default(),
line: [0u8;32]
}
}
}
impl<T, S> Deref for ZxPrinterDevice<T, S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.spooler
}
}
impl<T, S> DerefMut for ZxPrinterDevice<T, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.spooler
}
}
impl<T: FrameTimestamp, S: Spooler> ZxPrinterDevice<T, S> {
pub fn next_frame(&mut self) {
self.ready_ts = self.ready_ts.saturating_sub_frame();
}
pub fn reset(&mut self) {
self.motor = false;
self.ready = false;
for p in self.line.iter_mut() {
*p = 0;
}
self.cursor = 0;
}
pub fn write_control(&mut self, data: u8, timestamp: T) {
if data & MOTOR_MASK == 0 { self.start(data, timestamp);
}
else {
self.stop();
}
}
pub fn read_status(&mut self, timestamp: T) -> u8 {
if self.motor {
if self.ready || self.update_ready(timestamp) {
if self.cursor == 0 {
STATUS_NEW_LINE
}
else {
STATUS_BIT_READY
}
}
else {
STATUS_NOT_READY
}
}
else {
STATUS_OFF
}
}
fn update_ready(&mut self, timestamp: T) -> bool {
if timestamp > self.ready_ts {
self.ready = true;
return true;
}
false
}
#[inline]
fn flush(&mut self) {
if self.cursor != 0 {
let cursor = self.cursor;
self.cursor = 0;
let mask = 0x80 >> (cursor.wrapping_sub(1) & 7);
self.line[usize::from(cursor) >> 3] &= mask;
let index = (usize::from(cursor) + 7) >> 3;
self.spooler.push_line(&self.line[..index]);
}
}
fn stop(&mut self) {
if self.motor {
self.motor = false;
self.ready = false;
self.flush();
self.spooler.motor_off();
}
}
#[inline]
fn set_delay(&mut self, timestamp: T, data: u8) {
let mut delay: u32 = self.bit_delay.into();
if data & SLOW_MASK == SLOW_MASK {
delay *= 2;
}
self.ready_ts = timestamp + delay;
}
fn start(&mut self, data: u8, timestamp: T) {
if self.motor {
if self.ready {
self.write_bit(data & STYLUS_MASK == STYLUS_MASK);
self.set_delay(timestamp, data);
self.ready = false;
}
}
else {
self.motor = true;
self.ready = false;
self.cursor = 0;
self.set_delay(timestamp, data);
self.spooler.motor_on();
}
}
fn write_bit(&mut self, stylus: bool) {
let cursor = self.cursor;
let index = usize::from(cursor) >> 3;
let mask = 0x80 >> (cursor & 7);
if stylus {
self.line[index] |= mask;
}
else {
self.line[index] &= !mask;
}
let (cursor, over) = cursor.overflowing_add(1);
self.cursor = cursor;
if over {
self.spooler.push_line(&self.line);
}
}
}
impl Spooler for DebugSpooler {
fn push_line(&mut self, line: &[u8]) {
for b in line.iter().copied() {
print!("{:02x}", b);
}
println!();
}
}