use bitvec::prelude::*;
use log::trace;
use serde::{Deserialize, Serialize};
use crate::{
consts::INT_SERIAL,
context,
interface::LinkCable,
util::{pack, trait_alias},
};
#[derive(Default, Serialize, Deserialize)]
pub struct SerialTransfer {
buf: u8,
recv_buf: Option<u8>,
transfer_progress: bool,
use_internal_clock: bool,
transfer_timer: u64,
transfer_pos: usize,
#[serde(skip)]
link_cable: Option<Box<dyn LinkCable + Send + Sync>>,
}
trait_alias!(pub trait Context = context::InterruptFlag);
impl SerialTransfer {
pub fn set_link_cable(&mut self, link_cable: Option<Box<dyn LinkCable + Send + Sync>>) {
self.link_cable = link_cable;
}
pub fn tick(&mut self, ctx: &mut impl Context) {
if !self.transfer_progress {
return;
}
if self.recv_buf.is_none() {
if let Some(r) = &mut self.link_cable {
self.recv_buf = r.try_recv();
}
}
let mut done = false;
if self.use_internal_clock {
self.transfer_timer += 1;
if self.transfer_timer == 128 {
self.transfer_timer = 0;
self.transfer_pos += 1;
if self.transfer_pos == 8 {
done = true;
}
}
} else {
if self.recv_buf.is_some() {
done = true;
}
}
if done {
self.buf = self.recv_buf.unwrap_or(!0);
self.recv_buf = None;
self.transfer_pos = 0;
self.transfer_progress = false;
ctx.set_interrupt_flag_bit(INT_SERIAL);
}
}
pub fn read_sb(&mut self) -> u8 {
trace!("Read SB = ${:02X}", self.buf);
self.buf
}
pub fn write_sb(&mut self, data: u8) {
trace!("Write SB = ${data:02x}");
self.buf = data;
}
pub fn read_sc(&mut self) -> u8 {
let data = pack! {
7 => self.transfer_progress,
1..=6 => !0,
0 => self.use_internal_clock,
};
trace!("Read SB = ${data:02X}");
data
}
pub fn write_sc(&mut self, data: u8) {
trace!("Write SC = ${data:02x}");
let v = data.view_bits::<Lsb0>();
self.use_internal_clock = v[0];
if v[7] {
if let Some(link_cable) = &mut self.link_cable {
link_cable.send(self.buf);
}
self.transfer_progress = true;
self.transfer_timer = 0;
self.transfer_pos = 0;
self.recv_buf = None;
} else {
self.transfer_progress = false;
}
}
}