use std::sync::mpsc;
use tracing::{trace, warn};
use crate::{Cpu, CpuContext, sfr::*};
use crate::{CpuView, PortMapper};
pub struct Serial {
input_queue: mpsc::Receiver<u8>,
send_queue: mpsc::Sender<u8>,
sbuf_pending_read: u8,
recv_tick_count: u16,
sbuf_read: u8,
sbuf_pending_write: u8,
send_tick_count: u16,
sbuf_send_double_buffer: Option<u8>,
baud_rate_ticks: u16,
scon: u8,
}
pub const SCON_SM0: u8 = 1 << 7;
pub const SCON_SM1: u8 = 1 << 6;
pub const SCON_SM2: u8 = 1 << 5;
pub const SCON_REN: u8 = 1 << 4;
pub const SCON_TB8: u8 = 1 << 3;
pub const SCON_RB8: u8 = 1 << 2;
pub const SCON_TI: u8 = 1 << 1;
pub const SCON_RI: u8 = 1 << 0;
impl Serial {
pub fn new(baud_rate_ticks: u16) -> (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_pending_read: 0,
sbuf_pending_write: 0,
sbuf_read: 0,
scon: 0,
recv_tick_count: 0,
send_tick_count: 0,
baud_rate_ticks,
sbuf_send_double_buffer: None,
},
in_tx,
out_rx,
)
}
pub fn tick(&mut self, _cpu: &mut Cpu) {
if self.send_tick_count > 0 {
self.send_tick_count -= 1;
if self.send_tick_count == 0 {
_ = self.send_queue.send(self.sbuf_pending_write);
trace!(
"Serial: TX complete {:02X}, set TI",
self.sbuf_pending_write
);
self.sbuf_pending_write = 0;
self.scon |= SCON_TI;
if let Some(value) = self.sbuf_send_double_buffer.take() {
trace!("Serial: TX sending buffered value {:02X}", value);
self.sbuf_pending_write = value;
self.send_tick_count = self.baud_rate_ticks * bits_per_frame(self.scon);
}
}
}
if self.recv_tick_count > 0 {
self.recv_tick_count -= 1;
if self.recv_tick_count == 0 {
if (self.scon & SCON_REN) != 0 {
if self.scon & SCON_RI == 0 {
self.sbuf_read = self.sbuf_pending_read;
self.scon |= SCON_RI;
trace!("Serial: RX complete {:02X}, set RI", self.sbuf_read);
} else {
trace!("Serial: RX ignored, RI is already set");
}
} else {
trace!("Serial: RX ignored, REN is not set");
}
}
}
if self.recv_tick_count == 0
&& let Ok(value) = self.input_queue.try_recv()
{
trace!("Serial: RX started {:02X}", value);
self.sbuf_pending_read = value;
self.recv_tick_count = self.baud_rate_ticks * bits_per_frame(self.scon);
}
}
}
fn bits_per_frame(scon: u8) -> u16 {
let sm0 = (scon & SCON_SM0) != 0;
let sm1 = (scon & SCON_SM1) != 0;
match (sm0, sm1) {
(false, false) => 8, (false, true) => 10, (true, false) => 9, (true, true) => 11, }
}
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 => self.scon,
SFR_SBUF => {
trace!("Serial: SBUF read by CPU: {:02X}", self.sbuf_read);
self.sbuf_read
}
_ => 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 => {
trace!("Serial: SCON write {:02X}", value);
self.scon = value;
}
SFR_SBUF => {
if self.send_tick_count == 0 {
let ticks = self.baud_rate_ticks * bits_per_frame(self.scon);
trace!(
"Serial: SBUF write, TX started {:02X} ({} ticks)",
value, ticks
);
self.send_tick_count = ticks;
self.sbuf_pending_write = value;
} else {
if self.sbuf_send_double_buffer.is_some() {
trace!("Serial: SBUF write, TX double-buffer lost! {:02X}", value);
} else {
trace!("Serial: SBUF write, TX double-buffered {:02X}", value);
}
self.sbuf_send_double_buffer = Some(value);
}
}
_ => unreachable!(),
}
}
}
pub const P3_RXD: u8 = 1 << 0;
pub const P3_TXD: u8 = 1 << 1;
pub const P3_INT0: u8 = 1 << 2;
pub const P3_INT1: u8 = 1 << 3;
pub const P3_T0: u8 = 1 << 4;
pub const P3_T1: u8 = 1 << 5;
pub const P3_WR: u8 = 1 << 6;
pub const P3_RD: u8 = 1 << 7;
pub const TCON_TF1: u8 = 1 << 7;
pub const TCON_TR1: u8 = 1 << 6;
pub const TCON_TF0: u8 = 1 << 5;
pub const TCON_TR0: u8 = 1 << 4;
pub const TCON_IE1: u8 = 1 << 3;
pub const TCON_IT1: u8 = 1 << 2;
pub const TCON_IE0: u8 = 1 << 1;
pub const TCON_IT0: u8 = 1 << 0;
pub const TMOD_GATE1: u8 = 1 << 7;
pub const TMOD_C_T1: u8 = 1 << 6;
pub const TMOD_M11: u8 = 1 << 5;
pub const TMOD_M10: u8 = 1 << 4;
pub const TMOD_GATE0: u8 = 1 << 3;
pub const TMOD_C_T0: u8 = 1 << 2;
pub const TMOD_M01: u8 = 1 << 1;
pub const TMOD_M00: u8 = 1 << 0;
#[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 & TCON_TR0) != 0;
let tc0 = (self.tmod & TMOD_C_T0) != 0;
let gate0 = (self.tmod & TMOD_GATE0) != 0;
let tr1 = (self.tcon & TCON_TR1) != 0;
let tc1 = (self.tmod & TMOD_C_T1) != 0;
let gate1 = (self.tmod & TMOD_GATE1) != 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 & P3_INT0) != 0;
let int1 = (p3 & P3_INT1) != 0;
let t0 = (p3 & P3_T0) != 0;
let t1 = (p3 & P3_T1) != 0;
let mut res = TimerTick {
tick_t0: false,
tick_t1: false,
p3,
};
if tr0 && (!gate0 || int0) {
if tc0 {
res.tick_t0 = self.prev_p3 & P3_T0 != 0 && !t0; } else {
res.tick_t0 = true;
}
}
if tr1 && (!gate1 || int1) {
if tc1 {
res.tick_t1 = self.prev_p3 & P3_T1 != 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 |= TCON_TF0;
}
}
2 => {
self.tl0 = self.tl0.wrapping_add(1);
if self.tl0 == 0 {
self.tl0 = self.th0;
self.tcon |= TCON_TF0;
}
}
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 |= TCON_TF1;
}
}
2 => {
self.tl1 = self.tl1.wrapping_add(1);
if self.tl1 == 0 {
self.tl1 = self.th1;
self.tcon |= TCON_TF1;
}
}
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 => self.tcon,
SFR_TMOD => self.tmod,
SFR_TH0 => self.th0,
SFR_TL0 => self.tl0,
SFR_TH1 => self.th1,
SFR_TL1 => 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 => {
if self.tmod != value {
trace!("Timer mode changed: {:02X} -> {:02X}", self.tmod, value);
}
self.tmod = value;
}
SFR_TH0 => {
self.th0 = value;
}
SFR_TL0 => {
self.tl0 = value;
}
SFR_TH1 => {
self.th1 = value;
}
SFR_TL1 => {
self.tl1 = value;
}
_ => {
unreachable!()
}
}
}
}