Provide rx queues for convenience

NOTE: This is a breaking change.

Previously, callers of rx_char and rx_keyboard would have to check
output to see if the UART was actually ready to receive a character.
This change adds a receive queue to the DUART implementation that will
accept characters regardless of whether the DUART is ready to receive
or not. The service routine will then read fromn the queue if and only
if the DUART is ready to transmit.
This commit is contained in:
Seth Morabito 2018-12-17 17:17:59 -07:00
parent 7bf878bf9e
commit 048cfae67b
4 changed files with 73 additions and 113 deletions

View File

@ -2,7 +2,6 @@ use crate::err::BusError;
use crate::mem::Mem;
use crate::duart::Duart;
use crate::mouse::Mouse;
use crate::err::DuartError;
use std::fmt::Debug;
use std::ops::Range;
@ -195,16 +194,12 @@ impl Bus {
self.duart.tx_poll()
}
pub fn rx_char(&mut self, char: u8) -> Result<(), DuartError> {
self.duart.rx_char(char)
pub fn rx_char(&mut self, char: u8) {
self.duart.rx_char(char);
}
pub fn rx_keyboard(&mut self, keycode: u8) -> Result<(), DuartError> {
self.duart.rx_keyboard(keycode)
}
pub fn rx_ready(&self) -> bool {
self.duart.rx_ready()
pub fn rx_keyboard(&mut self, keycode: u8) {
self.duart.rx_keyboard(keycode);
}
pub fn duart_output(&self) -> u8 {

View File

@ -4,7 +4,6 @@ use crate::err::BusError;
use crate::err::CpuError;
use crate::rom_hi::HI_ROM;
use crate::rom_lo::LO_ROM;
use crate::err::DuartError;
pub struct Dmd {
cpu: Cpu,
@ -106,22 +105,15 @@ impl Dmd {
///
/// Receive a character from the host to the terminal.
///
pub fn rx_char(&mut self, character: u8) -> Result<(), DuartError> {
self.bus.rx_char(character)
pub fn rx_char(&mut self, character: u8) {
self.bus.rx_char(character);
}
///
/// Receive a character from the keyboard to the terminal.
///
pub fn rx_keyboard(&mut self, keycode: u8) -> Result<(), DuartError> {
self.bus.rx_keyboard(keycode)
}
///
/// Check to see if the terminal is ready to receive a new character.
///
pub fn rx_ready(&self) -> bool {
self.bus.rx_ready()
pub fn rx_keyboard(&mut self, keycode: u8) {
self.bus.rx_keyboard(keycode);
}
///

View File

@ -1,7 +1,6 @@
use crate::bus::AccessCode;
use crate::bus::Device;
use crate::err::BusError;
use crate::err::DuartError;
use std::fmt::Debug;
use std::fmt::Error;
@ -100,8 +99,8 @@ struct Port {
rx_data: u8,
tx_data: u8,
mode_ptr: usize,
rx_pending: bool,
tx_pending: bool,
rx_queue: VecDeque<u8>,
tx_queue: VecDeque<u8>,
char_delay: Duration,
next_rx: Instant,
next_tx: Instant,
@ -117,7 +116,6 @@ pub struct Duart {
imr: u8,
ivec: u8,
last_vblank: Instant,
tx_queue: VecDeque<u8>,
}
impl Duart {
@ -131,8 +129,8 @@ impl Duart {
rx_data: 0,
tx_data: 0,
mode_ptr: 0,
rx_pending: false,
tx_pending: false,
rx_queue: VecDeque::new(),
tx_queue: VecDeque::new(),
char_delay: Duration::new(0, 1_000_000),
next_rx: Instant::now(),
next_tx: Instant::now(),
@ -144,8 +142,8 @@ impl Duart {
rx_data: 0,
tx_data: 0,
mode_ptr: 0,
rx_pending: false,
tx_pending: false,
rx_queue: VecDeque::new(),
tx_queue: VecDeque::new(),
char_delay: Duration::new(0, 1_000_000),
next_rx: Instant::now(),
next_tx: Instant::now(),
@ -159,7 +157,6 @@ impl Duart {
imr: 0,
ivec: 0,
last_vblank: Instant::now(),
tx_queue: VecDeque::new(),
}
}
@ -180,18 +177,51 @@ impl Duart {
}
}
fn handle_rx(&mut self, port: usize) {
let mut ctx = &mut self.ports[port];
let (istat, ivec) = match port {
0 => (ISTS_RAI, RX_INT),
_ => (ISTS_RBI, KEYBOARD_INT),
};
if !ctx.rx_queue.is_empty() && Instant::now() >= ctx.next_rx {
match ctx.rx_queue.pop_back() {
Some(c) => {
if ctx.conf & CNF_ERX != 0 {
ctx.rx_data = c;
ctx.stat |= STS_RXR;
self.istat |= istat;
self.ivec |= ivec;
} else {
ctx.stat |= STS_OER;
}
},
None => {
// This is really unexpected! We just asserted
// that the queue was not empty, so this should
// really never happen.
}
}
if !ctx.rx_queue.is_empty() {
ctx.next_rx = Instant::now() + ctx.char_delay;
}
}
}
pub fn service(&mut self) {
let mut ctx = &mut self.ports[PORT_0];
if ctx.tx_pending && Instant::now() >= ctx.next_tx {
// Finish our transmit.
// Deal with RS-232 Transmit
if (ctx.conf & CNF_ETX) != 0 &&
(ctx.stat & STS_TXR) == 0 &&
(ctx.stat & STS_TXE) == 0 && Instant::now() >= ctx.next_tx {
let c = ctx.tx_data;
ctx.conf |= CNF_ETX;
ctx.stat |= STS_TXR;
ctx.stat |= STS_TXE;
self.istat |= ISTS_TAI;
self.ivec |= TX_INT;
ctx.tx_pending = false;
if (ctx.mode[1] >> 6) & 3 == 0x2 {
// Loopback Mode.
ctx.rx_data = c;
@ -199,9 +229,17 @@ impl Duart {
self.istat |= ISTS_RAI;
self.ivec |= RX_INT;
} else {
self.tx_queue.push_front(c);
ctx.tx_queue.push_front(c);
}
// ctx.next_tx = Instant::now() + ctx.char_delay;
}
// Deal with RS-232 Receive
self.handle_rx(PORT_0);
// Deal with Keyboard Receive
self.handle_rx(PORT_1);
}
pub fn vertical_blank(&mut self) {
@ -222,7 +260,7 @@ impl Duart {
}
pub fn tx_poll(&mut self) -> Option<u8> {
self.tx_queue.pop_back()
self.ports[PORT_0].tx_queue.pop_back()
}
pub fn mouse_down(&mut self, button: u8) {
@ -266,60 +304,24 @@ impl Duart {
}
}
pub fn rx_ready(&self) -> bool {
let ctx = &self.ports[PORT_0];
return (ctx.stat & STS_RXR) != 0;
}
pub fn rx_keyboard(&mut self, c: u8) -> Result<(), DuartError> {
pub fn rx_keyboard(&mut self, c: u8) {
let mut ctx = &mut self.ports[PORT_1];
if ctx.rx_pending {
if Instant::now() > ctx.next_rx {
if ctx.conf & CNF_ERX != 0 {
ctx.rx_pending = false;
ctx.rx_data = c;
ctx.stat |= STS_RXR;
self.istat |= ISTS_RBI;
self.ivec |= KEYBOARD_INT;
} else {
ctx.stat |= STS_OER;
}
Ok(())
} else {
Err(DuartError::ReceiverNotReady)
}
} else {
if ctx.rx_queue.is_empty() {
ctx.next_rx = Instant::now() + ctx.char_delay;
ctx.rx_pending = true;
Err(DuartError::ReceiverNotReady)
}
ctx.rx_queue.push_front(c);
}
pub fn rx_char(&mut self, c: u8) -> Result<(), DuartError> {
pub fn rx_char(&mut self, c: u8) {
let mut ctx = &mut self.ports[PORT_0];
if ctx.rx_pending {
if Instant::now() > ctx.next_rx {
if ctx.conf & CNF_ERX != 0 {
ctx.rx_pending = false;
ctx.rx_data = c;
ctx.stat |= STS_RXR;
self.istat |= ISTS_RAI;
self.ivec |= RX_INT;
} else {
ctx.stat |= STS_OER;
}
Ok(())
} else {
Err(DuartError::ReceiverNotReady)
}
} else {
if ctx.rx_queue.is_empty() {
ctx.next_rx = Instant::now() + ctx.char_delay;
ctx.rx_pending = true;
Err(DuartError::ReceiverNotReady)
}
ctx.rx_queue.push_front(c);
}
pub fn handle_command(&mut self, cmd: u8, port: usize) {
@ -484,12 +486,10 @@ impl Device for Duart {
THRA => {
let mut ctx = &mut self.ports[PORT_0];
ctx.tx_data = val;
// Update state. Since we're transmitting,
// the transmitter buffer is not empty.
// The actual transmit will happen in the 'service'
// function.
// Update state. Since we're transmitting, the
// transmitter buffer is not empty. The actual
// transmit will happen in the 'service' function.
ctx.next_tx = Instant::now() + ctx.char_delay;
ctx.tx_pending = true;
ctx.stat &= !(STS_TXE | STS_TXR);
self.ivec &= !TX_INT;
self.istat &= !ISTS_TAI;

View File

@ -1,6 +1,5 @@
use std::error::Error;
use std::fmt;
use std::fmt::Formatter;
#[derive(Debug)]
pub enum CpuException {
@ -128,35 +127,9 @@ impl From<CpuException> for CpuError {
CpuError::Exception(err)
}
}
impl From<BusError> for CpuError {
fn from(err: BusError) -> CpuError {
CpuError::Bus(err)
}
}
#[derive(Debug)]
pub enum DuartError {
ReceiverNotReady
}
impl fmt::Display for DuartError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
DuartError::ReceiverNotReady => write!(f, "receiver not ready")
}
}
}
impl Error for DuartError {
fn description(&self) -> &str {
match *self {
DuartError::ReceiverNotReady => "Receiver not ready",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
DuartError::ReceiverNotReady => None
}
}
}