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"
|
env_logger = "0.9.0"
|
||||||
lazy_static = "~1.4"
|
lazy_static = "~1.4"
|
||||||
libc = "~0.2"
|
libc = "~0.2"
|
||||||
ringbuffer = "0.8.5"
|
thiserror = "1.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
|
@ -14,7 +14,7 @@ lazy_static! {
|
||||||
pub static ref DMD: Mutex<Dmd> = Mutex::new(Dmd::new());
|
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 SUCCESS: c_int = 0;
|
||||||
const ERROR: c_int = 1;
|
const ERROR: c_int = 1;
|
||||||
const BUSY: c_int = 2;
|
const BUSY: c_int = 2;
|
||||||
|
|
34
src/duart.rs
34
src/duart.rs
|
@ -38,10 +38,8 @@
|
||||||
use crate::bus::{AccessCode, Device};
|
use crate::bus::{AccessCode, Device};
|
||||||
use crate::err::BusError;
|
use crate::err::BusError;
|
||||||
|
|
||||||
|
use crate::utils::FifoQueue;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use ringbuffer::{
|
|
||||||
ConstGenericRingBuffer, RingBuffer, RingBufferExt, RingBufferRead, RingBufferWrite,
|
|
||||||
};
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fmt::Error;
|
use std::fmt::Error;
|
||||||
|
@ -141,14 +139,6 @@ const MOUSE_BLANK_INT: u8 = 0x02;
|
||||||
const TX_INT: u8 = 0x10;
|
const TX_INT: u8 = 0x10;
|
||||||
const RX_INT: u8 = 0x20;
|
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 {
|
struct Port {
|
||||||
// Mode, Status, and Configuration registers
|
// Mode, Status, and Configuration registers
|
||||||
mode: [u8; 2],
|
mode: [u8; 2],
|
||||||
|
@ -156,7 +146,7 @@ struct Port {
|
||||||
stat: u8,
|
stat: u8,
|
||||||
conf: u8,
|
conf: u8,
|
||||||
// State used by the RX and TX state machines.
|
// 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>,
|
rx_shift_reg: Option<u8>,
|
||||||
tx_holding_reg: Option<u8>,
|
tx_holding_reg: Option<u8>,
|
||||||
tx_shift_reg: Option<u8>,
|
tx_shift_reg: Option<u8>,
|
||||||
|
@ -177,7 +167,7 @@ impl Port {
|
||||||
mode_ptr: 0,
|
mode_ptr: 0,
|
||||||
stat: 0,
|
stat: 0,
|
||||||
conf: 0,
|
conf: 0,
|
||||||
rx_fifo: ConstGenericRingBuffer::<u8, RX_FIFO_INIT_LEN>::new(),
|
rx_fifo: FifoQueue::new(),
|
||||||
rx_shift_reg: None,
|
rx_shift_reg: None,
|
||||||
tx_holding_reg: None,
|
tx_holding_reg: None,
|
||||||
tx_shift_reg: None,
|
tx_shift_reg: None,
|
||||||
|
@ -195,18 +185,20 @@ impl Port {
|
||||||
/// slot. If a character is currently in the shift register, it is
|
/// slot. If a character is currently in the shift register, it is
|
||||||
/// moved into the FIFO now that there is room.
|
/// moved into the FIFO now that there is room.
|
||||||
fn rx_read_char(&mut self) -> Option<u8> {
|
fn rx_read_char(&mut self) -> Option<u8> {
|
||||||
if self.rx_enabled() {
|
if !self.rx_enabled() {
|
||||||
let val = self.rx_fifo.dequeue();
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(val) = self.rx_fifo.pop() {
|
||||||
// The FIFO is no longer full (even if only temporarily)
|
// The FIFO is no longer full (even if only temporarily)
|
||||||
self.stat &= !STS_FFL;
|
self.stat &= !STS_FFL;
|
||||||
|
|
||||||
// If the RX shift register held a character, it's time
|
// If the RX shift register held a character, it's time
|
||||||
// to move it into the FIFO.
|
// to move it into the FIFO.
|
||||||
if let Some(c) = self.rx_shift_reg {
|
if let Some(c) = self.rx_shift_reg {
|
||||||
self.rx_fifo.push(c);
|
let _ = self.rx_fifo.push(c);
|
||||||
self.stat |= STS_RXR;
|
self.stat |= STS_RXR;
|
||||||
if self.rx_fifo.len() >= RX_FIFO_LEN {
|
if self.rx_fifo.is_full() {
|
||||||
self.stat |= STS_FFL;
|
self.stat |= STS_FFL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +211,7 @@ impl Port {
|
||||||
// Reset Parity Error and Received Break on read
|
// Reset Parity Error and Received Break on read
|
||||||
self.stat &= !(STS_PER | STS_RXB);
|
self.stat &= !(STS_PER | STS_RXB);
|
||||||
|
|
||||||
val
|
Some(val)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -233,9 +225,9 @@ impl Port {
|
||||||
/// new character is received while the FIFO is full.
|
/// new character is received while the FIFO is full.
|
||||||
fn rx_char(&mut self, c: u8) {
|
fn rx_char(&mut self, c: u8) {
|
||||||
trace!("rx_char: {:02x}, fifo_len={}", c, self.rx_fifo.len());
|
trace!("rx_char: {:02x}, fifo_len={}", c, self.rx_fifo.len());
|
||||||
if self.rx_fifo.len() < RX_FIFO_LEN {
|
if !self.rx_fifo.is_full() {
|
||||||
self.rx_fifo.push(c);
|
let _ = self.rx_fifo.push(c);
|
||||||
if self.rx_fifo.len() >= RX_FIFO_LEN {
|
if self.rx_fifo.is_full() {
|
||||||
trace!("FIFO FULL.");
|
trace!("FIFO FULL.");
|
||||||
self.stat |= STS_FFL;
|
self.stat |= STS_FFL;
|
||||||
}
|
}
|
||||||
|
|
26
src/lib.rs
26
src/lib.rs
|
@ -1,13 +1,19 @@
|
||||||
pub mod bus;
|
#[allow(unused)]
|
||||||
pub mod cpu;
|
mod bus;
|
||||||
pub mod dmd;
|
#[allow(unused)]
|
||||||
pub mod duart;
|
mod cpu;
|
||||||
pub mod err;
|
#[allow(unused)]
|
||||||
pub mod instr;
|
mod dmd;
|
||||||
pub mod mem;
|
mod duart;
|
||||||
pub mod mouse;
|
#[allow(unused)]
|
||||||
pub mod rom_hi;
|
mod err;
|
||||||
pub mod rom_lo;
|
#[allow(unused)]
|
||||||
|
mod instr;
|
||||||
|
mod mem;
|
||||||
|
mod mouse;
|
||||||
|
mod rom_hi;
|
||||||
|
mod rom_lo;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
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