diff --git a/Cargo.toml b/Cargo.toml index a9b2abb..97a9ed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ log = { version = "0.4.8", features = ["std"] } env_logger = "0.9.0" lazy_static = "~1.4" libc = "~0.2" -ringbuffer = "0.8.5" +thiserror = "1.0" [profile.release] debug = true diff --git a/src/dmd.rs b/src/dmd.rs index 0ec57a4..8acf5fb 100644 --- a/src/dmd.rs +++ b/src/dmd.rs @@ -14,7 +14,7 @@ lazy_static! { pub static ref DMD: Mutex = Mutex::new(Dmd::new()); } -// Return vlaues for the C library +// Return values for the C library const SUCCESS: c_int = 0; const ERROR: c_int = 1; const BUSY: c_int = 2; diff --git a/src/duart.rs b/src/duart.rs index 0d8a9ba..f80fa28 100644 --- a/src/duart.rs +++ b/src/duart.rs @@ -38,10 +38,8 @@ use crate::bus::{AccessCode, Device}; use crate::err::BusError; +use crate::utils::FifoQueue; use log::{debug, trace}; -use ringbuffer::{ - ConstGenericRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite, -}; use std::collections::VecDeque; use std::fmt::Debug; use std::fmt::Error; @@ -141,14 +139,6 @@ const MOUSE_BLANK_INT: u8 = 0x02; const TX_INT: u8 = 0x10; const RX_INT: u8 = 0x20; -// NB: The terrible Ring Buffer library requries capacities with a -// power of 2, so we have to have extra logic to deal with this! -// -// TODO: Pick a new ring buffer library or just implement our own -// 3-slot FIFO. -const RX_FIFO_LEN: usize = 3; -const RX_FIFO_INIT_LEN: usize = 4; - struct Port { // Mode, Status, and Configuration registers mode: [u8; 2], @@ -156,7 +146,7 @@ struct Port { stat: u8, conf: u8, // State used by the RX and TX state machines. - rx_fifo: ConstGenericRingBuffer, + rx_fifo: FifoQueue, rx_shift_reg: Option, tx_holding_reg: Option, tx_shift_reg: Option, @@ -177,7 +167,7 @@ impl Port { mode_ptr: 0, stat: 0, conf: 0, - rx_fifo: ConstGenericRingBuffer::::new(), + rx_fifo: FifoQueue::new(), rx_shift_reg: None, tx_holding_reg: None, tx_shift_reg: None, @@ -195,18 +185,20 @@ impl Port { /// slot. If a character is currently in the shift register, it is /// moved into the FIFO now that there is room. fn rx_read_char(&mut self) -> Option { - if self.rx_enabled() { - let val = self.rx_fifo.dequeue(); + if !self.rx_enabled() { + return None; + } + if let Ok(val) = self.rx_fifo.pop() { // The FIFO is no longer full (even if only temporarily) self.stat &= !STS_FFL; // If the RX shift register held a character, it's time // to move it into the FIFO. if let Some(c) = self.rx_shift_reg { - self.rx_fifo.push(c); + let _ = self.rx_fifo.push(c); self.stat |= STS_RXR; - if self.rx_fifo.len() >= RX_FIFO_LEN { + if self.rx_fifo.is_full() { self.stat |= STS_FFL; } } @@ -219,7 +211,7 @@ impl Port { // Reset Parity Error and Received Break on read self.stat &= !(STS_PER | STS_RXB); - val + Some(val) } else { None } @@ -233,9 +225,9 @@ impl Port { /// new character is received while the FIFO is full. fn rx_char(&mut self, c: u8) { trace!("rx_char: {:02x}, fifo_len={}", c, self.rx_fifo.len()); - if self.rx_fifo.len() < RX_FIFO_LEN { - self.rx_fifo.push(c); - if self.rx_fifo.len() >= RX_FIFO_LEN { + if !self.rx_fifo.is_full() { + let _ = self.rx_fifo.push(c); + if self.rx_fifo.is_full() { trace!("FIFO FULL."); self.stat |= STS_FFL; } diff --git a/src/lib.rs b/src/lib.rs index fd98002..b9aaf30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,19 @@ -pub mod bus; -pub mod cpu; -pub mod dmd; -pub mod duart; -pub mod err; -pub mod instr; -pub mod mem; -pub mod mouse; -pub mod rom_hi; -pub mod rom_lo; +#[allow(unused)] +mod bus; +#[allow(unused)] +mod cpu; +#[allow(unused)] +mod dmd; +mod duart; +#[allow(unused)] +mod err; +#[allow(unused)] +mod instr; +mod mem; +mod mouse; +mod rom_hi; +mod rom_lo; +mod utils; #[macro_use] extern crate lazy_static; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..1eb51c6 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,116 @@ +use thiserror::Error; + +const FIFO_LEN: usize = 3; + +/// A simple circular buffer with three slots, used as a +/// DUART character FIFO +pub struct FifoQueue { + buf: [u8; 3], + read_ptr: usize, + write_ptr: usize, + len: usize, +} + +#[derive(Error, Debug, Eq, PartialEq)] +pub enum FifoError { + #[error("fifo full")] + Overflow, + #[error("fifo under-run")] + Underrun, +} + +impl FifoQueue { + pub fn new() -> Self { + FifoQueue { + buf: [0; FIFO_LEN], + read_ptr: 0, + write_ptr: 0, + len: 0, + } + } + + pub fn push(&mut self, c: u8) -> Result<(), FifoError> { + if self.len == FIFO_LEN { + Err(FifoError::Overflow) + } else { + self.buf[self.write_ptr] = c; + self.write_ptr = (self.write_ptr + 1) % FIFO_LEN; + self.len += 1; + Ok(()) + } + } + + pub fn pop(&mut self) -> Result { + if self.len == 0 { + Err(FifoError::Underrun) + } else { + let c = self.buf[self.read_ptr]; + self.read_ptr = (self.read_ptr + 1) % FIFO_LEN; + self.len -= 1; + Ok(c) + } + } + + pub fn clear(&mut self) { + self.read_ptr = 0; + self.write_ptr = 0; + self.len = 0; + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + pub fn is_full(&self) -> bool { + self.len == FIFO_LEN + } + + pub fn len(&self) -> usize { + self.len + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn pops_in_order() { + let mut f: FifoQueue = FifoQueue::new(); + + assert_eq!(Ok(()), f.push(1)); + assert_eq!(Ok(()), f.push(2)); + assert_eq!(Ok(()), f.push(3)); + assert_eq!(Ok(1), f.pop()); + assert_eq!(Ok(2), f.pop()); + assert_eq!(Ok(3), f.pop()); + } + + #[test] + fn popping_when_empty_returns_underrun_error() { + let mut f: FifoQueue = FifoQueue::new(); + + assert_eq!(0, f.len()); + assert_eq!(Err(FifoError::Underrun), f.pop()); + + assert_eq!(Ok(()), f.push(42)); + assert_eq!(1, f.len()); + assert_eq!(Ok(42), f.pop()); + assert_eq!(Err(FifoError::Underrun), f.pop()); + } + + #[test] + fn pushing_when_full_returns_overflow_error() { + let mut f: FifoQueue = FifoQueue::new(); + + assert_eq!(0, f.len()); + assert_eq!(Ok(()), f.push(0xff)); + assert_eq!(1, f.len()); + assert_eq!(Ok(()), f.push(0xfe)); + assert_eq!(2, f.len()); + assert_eq!(Ok(()), f.push(0xfd)); + assert_eq!(3, f.len()); + assert_eq!(Err(FifoError::Overflow), f.push(0xfc)); + assert_eq!(3, f.len()); + } +}