use std::cell::RefCell;
use std::sync::mpsc;
use tracing::warn;
use crate::{Cpu, CpuContext, Interrupt, sfr::*};
use crate::{CpuView, PortMapper};
pub struct Serial {
input_queue: mpsc::Receiver<u8>,
send_queue: mpsc::Sender<u8>,
sbuf_read: RefCell<Option<u8>>,
sbuf_write: Option<u8>,
recv_tick_delay: RefCell<u16>,
send_tick_delay: u16,
scon: u8,
}
impl Serial {
pub fn new() -> (Self, mpsc::Sender<u8>, mpsc::Receiver<u8>) {
let (in_tx, in_rx) = mpsc::channel();
let (out_tx, out_rx) = mpsc::channel();
(
Self {
input_queue: in_rx,
send_queue: out_tx,
sbuf_read: RefCell::new(None),
sbuf_write: None,
scon: 1 << 4,
recv_tick_delay: RefCell::new(0),
send_tick_delay: 0,
},
in_tx,
out_rx,
)
}
pub fn tick(&mut self) {
if let Some(value) = self.sbuf_write {
self.send_tick_delay = self.send_tick_delay.wrapping_add(1);
if self.send_tick_delay >= 10 {
self.send_tick_delay = 0;
if let Ok(()) = self.send_queue.send(value) {
self.sbuf_write = None;
self.scon |= 1 << 1;
}
}
}
if self.scon & (1 << 4) != 0 {
let mut delay = self.recv_tick_delay.borrow_mut();
*delay = delay.wrapping_add(1);
if *delay >= 20 {
*delay = 0;
if let Ok(value) = self.input_queue.try_recv() {
*self.sbuf_read.borrow_mut() = Some(value);
self.scon |= 1 << 0;
}
}
}
}
}
impl PortMapper for Serial {
type WriteValue = (u8, u8);
fn interest<C: CpuView>(&self, cpu: &C, addr: u8) -> bool {
addr == SFR_SCON || addr == SFR_SBUF
}
fn read<C: CpuView>(&self, cpu: &C, addr: u8) -> u8 {
match addr {
SFR_SCON => {
return self.scon;
}
SFR_SBUF => {
if let Some(value) = self.sbuf_read.borrow_mut().take() {
*self.recv_tick_delay.borrow_mut() = 0;
return value;
}
return 0;
}
_ => {
unreachable!()
}
}
}
fn prepare_write<C: CpuView>(&self, cpu: &C, addr: u8, value: u8) -> Self::WriteValue {
(addr, value)
}
fn write(&mut self, (addr, value): Self::WriteValue) {
match addr {
SFR_SCON => {
self.scon = value;
}
SFR_SBUF => {
self.sbuf_write = Some(value);
self.send_tick_delay = 0;
}
_ => {
unreachable!()
}
}
}
}
#[derive(Debug, Default)]
pub struct Timer {
tcon: u8,
tmod: u8,
th0: u8,
tl0: u8,
th1: u8,
tl1: u8,
prev_p3: u8,
warned_timer: bool,
}
pub struct TimerTick {
tick_t0: bool,
tick_t1: bool,
p3: u8,
}
impl Timer {
pub fn prepare_tick(&self, cpu: &mut Cpu, ctx: &impl CpuContext) -> TimerTick {
let tr0 = (self.tcon & (1 << 4)) != 0;
let tc0 = (self.tmod & (1 << 2)) != 0;
let gate0 = (self.tmod & (1 << 3)) != 0;
let tr1 = (self.tcon & (1 << 6)) != 0;
let tc1 = (self.tmod & (1 << 6)) != 0;
let gate1 = (self.tmod & (1 << 7)) != 0;
let needs_p3 = (tr0 && (tc0 || gate0)) || (tr1 && (tc1 || gate1));
let p3 = if needs_p3 {
cpu.sfr(SFR_P3, ctx)
} else {
self.prev_p3
};
let int0 = (p3 & (1 << 2)) != 0;
let int1 = (p3 & (1 << 3)) != 0;
let t0 = (p3 & (1 << 4)) != 0;
let t1 = (p3 & (1 << 5)) != 0;
let mut res = TimerTick {
tick_t0: false,
tick_t1: false,
p3,
};
if tr0 && (!gate0 || int0) {
if tc0 {
res.tick_t0 = self.prev_p3 & (1 << 4) != 0 && !t0; } else {
res.tick_t0 = true;
}
}
if tr1 && (!gate1 || int1) {
if tc1 {
res.tick_t1 = self.prev_p3 & (1 << 5) != 0 && !t1; } else {
res.tick_t1 = true;
}
}
res
}
pub fn tick(&mut self, cpu: &mut Cpu, tick: TimerTick) {
self.prev_p3 = tick.p3;
if tick.tick_t0 {
match self.tmod & 0x03 {
1 => {
self.tl0 = self.tl0.wrapping_add(1);
if self.tl0 == 0 {
self.th0 = self.th0.wrapping_add(1);
}
if self.th0 == 0 && self.tl0 == 0 {
self.tcon |= 1 << 5;
cpu.interrupt(Interrupt::Timer0);
}
}
mode => {
if !self.warned_timer {
warn!("Timer 0: Timer mode {mode} not supported");
self.warned_timer = true;
}
}
}
}
if tick.tick_t1 {
match (self.tmod & 0x30) >> 4 {
1 => {
self.tl1 = self.tl1.wrapping_add(1);
if self.tl1 == 0 {
self.th1 = self.th1.wrapping_add(1);
}
if self.th1 == 0 && self.tl1 == 0 {
self.tcon |= 1 << 7;
cpu.interrupt(Interrupt::Timer1);
}
}
mode => {
if !self.warned_timer {
warn!("Timer 1: Timer mode {mode} not supported");
self.warned_timer = true;
}
}
}
}
}
}
impl PortMapper for Timer {
type WriteValue = (u8, u8);
fn interest<C: CpuView>(&self, _cpu: &C, addr: u8) -> bool {
addr == SFR_TCON
|| addr == SFR_TMOD
|| addr == SFR_TH0
|| addr == SFR_TL0
|| addr == SFR_TH1
|| addr == SFR_TL1
}
fn read<C: CpuView>(&self, _cpu: &C, addr: u8) -> u8 {
match addr {
SFR_TCON => {
return self.tcon;
}
SFR_TMOD => {
return self.tmod;
}
SFR_TH0 => {
return self.th0;
}
SFR_TL0 => {
return self.tl0;
}
SFR_TH1 => {
return self.th1;
}
SFR_TL1 => {
return self.tl1;
}
_ => {
unreachable!()
}
}
}
fn prepare_write<C: CpuView>(&self, _cpu: &C, addr: u8, value: u8) -> Self::WriteValue {
(addr, value)
}
fn write(&mut self, (addr, value): Self::WriteValue) {
match addr {
SFR_TCON => {
self.tcon = value;
}
SFR_TMOD => {
self.tmod = value;
}
SFR_TH0 => {
self.th0 = value;
}
SFR_TL0 => {
self.tl0 = value;
}
SFR_TH1 => {
self.th1 = value;
}
SFR_TL1 => {
self.tl1 = value;
}
_ => {
unreachable!()
}
}
}
}