Remove dependency on ringbuffer
This change sees the introduction of a tiny FIFO buffer implementation in util.rs, in lieu of a dependency on the ringbuffer library. The reason for this change is largely because the ringbuffer library was unnecessarily complicated and generic, and could only work with sizes that were a power of 2.
This commit is contained in:
parent
73ea34e619
commit
d19f3d4fdb
|
@ -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
|
||||
|
|
|
@ -14,7 +14,7 @@ lazy_static! {
|
|||
pub static ref DMD: Mutex<Dmd> = 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;
|
||||
|
|
34
src/duart.rs
34
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<u8, RX_FIFO_INIT_LEN>,
|
||||
rx_fifo: FifoQueue,
|
||||
rx_shift_reg: Option<u8>,
|
||||
tx_holding_reg: Option<u8>,
|
||||
tx_shift_reg: Option<u8>,
|
||||
|
@ -177,7 +167,7 @@ impl Port {
|
|||
mode_ptr: 0,
|
||||
stat: 0,
|
||||
conf: 0,
|
||||
rx_fifo: ConstGenericRingBuffer::<u8, RX_FIFO_INIT_LEN>::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<u8> {
|
||||
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;
|
||||
}
|
||||
|
|
26
src/lib.rs
26
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;
|
||||
|
|
|
@ -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<u8, FifoError> {
|
||||
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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue