use crate::interrupts::{InterruptContext, InterruptFlags, Interrupts};
use log::{debug, trace};
use std::collections::VecDeque;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SerialStream {
to_gb_bytes: VecDeque<u8>,
from_gb_bytes: VecDeque<u8>,
}
impl SerialStream {
pub fn send_byte(&mut self, byte: u8) {
self.to_gb_bytes.push_back(byte);
}
pub fn send_slice(&mut self, bytes: &[u8]) {
self.to_gb_bytes.extend(bytes);
}
pub fn send_from<I>(&mut self, iter: I)
where
I: IntoIterator<Item = u8>,
{
self.to_gb_bytes.extend(iter);
}
pub fn receive_byte(&mut self) -> Option<u8> {
self.from_gb_bytes.pop_front()
}
pub fn receive_bytes(&mut self) -> impl '_ + Iterator<Item = u8> + ExactSizeIterator {
self.from_gb_bytes.drain(..)
}
fn pop_next_to_gb(&mut self) -> u8 {
self.to_gb_bytes.pop_front().unwrap_or_default()
}
fn set_from_gb(&mut self, byte: u8) {
self.from_gb_bytes.push_back(byte);
}
}
pub trait SerialContext: InterruptContext {
fn serial(&self) -> &SerialState;
fn serial_mut(&mut self) -> &mut SerialState;
fn serial_regs(&self) -> &SerialRegs;
fn serial_regs_mut(&mut self) -> &mut SerialRegs;
}
const SLOW_TPERIOD: u64 = 512;
#[allow(unused)]
const FAST_TPERIOD: u64 = 16;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct SerialRegs {
pub serial_data: u8,
pub serial_control: u8,
}
memdev_fields!(SerialRegs, len: 2, {
0x00 => serial_data,
0x01 => serial_control,
});
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct SerialState {
t_progress: u64,
bit_progress: u8,
remote_byte: u8,
pub stream: SerialStream,
}
impl SerialState {
pub fn new() -> SerialState {
Default::default()
}
}
pub fn tick(ctx: &mut impl SerialContext, tcycles: u64) {
trace!(
"Serial tick. Serial control field: {:#6x}, Serial data:{:#6x}",
ctx.serial_regs().serial_control,
ctx.serial_regs().serial_data,
);
if ctx.serial_regs().serial_control == 0x81 {
debug!(
"Serial transfer active. Progress: {} cycles",
ctx.serial().t_progress
);
if ctx.serial().bit_progress == 0 {
let serial = ctx.serial_mut();
serial.remote_byte = serial.stream.pop_next_to_gb();
}
ctx.serial_mut().t_progress += tcycles;
while ctx.serial().t_progress > SLOW_TPERIOD {
ctx.serial_mut().t_progress -= SLOW_TPERIOD;
ctx.serial_mut().bit_progress += 1;
let [new_local_byte, new_remote_byte] =
u16::from_ne_bytes([ctx.serial_regs().serial_data, ctx.serial().remote_byte])
.rotate_left(1)
.to_ne_bytes();
ctx.serial_regs_mut().serial_data = new_local_byte;
ctx.serial_mut().remote_byte = new_remote_byte;
if ctx.serial().bit_progress == 8 {
ctx.serial_regs_mut().serial_control = 0x01; ctx.interrupts_mut().send(InterruptFlags::SERIAL);
let serial = ctx.serial_mut();
serial.bit_progress = 0;
serial.stream.set_from_gb(serial.remote_byte);
}
}
}
}