Updated TX and RX polling

This commit is contained in:
Seth Morabito 2018-12-09 17:57:42 -08:00
parent badd42c2b6
commit 4816d3cdc0
5 changed files with 124 additions and 46 deletions

View File

@ -19,7 +19,11 @@ terminal, including:
- WE32100 CPU
- I/O
This project is written in Rust, and uses [Neon Bindings](https://github.com/neon-bindings/neon)
to compile down to a Node.js library for later inclusion in an Electron
JavaScript application that will present the user interface and display
drawing area.
Note that there is no user interface: This is a back-end library only.
It may be used as a component to build a fully-fledged emulator,
however.
## Emulator Reference Implementation
For a reference implementation emulator that uses this library,
please see the ["DMD" project on GitHub](https://github.com/sethm/dmd).

View File

@ -64,10 +64,10 @@ pub struct Bus {
}
impl Bus {
pub fn new<CB: 'static + FnMut(u8) + Send + Sync>(mem_size: usize, tx_callback: CB) -> Bus {
pub fn new(mem_size: usize) -> Bus {
Bus {
rom: Mem::new(0, 0x20000, true),
duart: Duart::new(tx_callback),
duart: Duart::new(),
scc: Mem::new(0x300000, 0x100, false),
mouse: Mouse::new(),
vid: Mem::new(0x500000, 0x2, false),
@ -176,10 +176,6 @@ impl Bus {
self.duart.get_interrupt()
}
pub fn keyboard(&mut self, keycode: u8) {
self.duart.handle_keyboard(keycode);
}
pub fn mouse_move(&mut self, x: u16, y: u16) {
self.mouse.x = x;
self.mouse.y = y;
@ -193,10 +189,18 @@ impl Bus {
self.duart.mouse_up(button);
}
pub fn tx_poll(&mut self) -> Option<u8> {
self.duart.tx_poll()
}
pub fn rx_char(&mut self, char: u8) -> Result<(), DuartError> {
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()
}
@ -206,11 +210,9 @@ impl Bus {
mod tests {
use super::*;
fn tx_callback(_char: u8) {}
#[test]
fn should_fail_on_alignment_errors() {
let mut bus: Bus = Bus::new(0x10000, tx_callback);
let mut bus: Bus = Bus::new(0x10000);
assert!(bus.write_byte(0x700000, 0x1f).is_ok());
assert!(bus.write_half(0x700000, 0x1f1f).is_ok());

View File

@ -2104,8 +2104,6 @@ mod tests {
const BASE: usize = 0x700000;
fn tx_callback(_char: u8) {}
/// Helper function to set up and prepare a cpu and bus
/// with a supplied program.
fn do_with_program<F>(program: &[u8], test: F)
@ -2113,7 +2111,7 @@ mod tests {
F: Fn(&mut Cpu, &mut Bus),
{
let mut cpu: Cpu = Cpu::new();
let mut bus: Bus = Bus::new(0x10000, tx_callback);
let mut bus: Bus = Bus::new(0x10000);
bus.load(BASE, &program).unwrap();
cpu.r[R_PC] = BASE as u32;

View File

@ -12,15 +12,21 @@ pub struct Dmd {
}
impl Dmd {
pub fn new<CB: 'static + FnMut(u8) + Send + Sync>(tx_callback: CB) -> Dmd {
///
/// Construct a new DMD Terminal
///
pub fn new() -> Dmd {
let cpu = Cpu::new();
let bus = Bus::new(0x100000, tx_callback);
let bus = Bus::new(0x100000);
Dmd {
cpu,
bus,
}
}
///
/// Reset the terminal's CPU. This is equivalent to a hard power reset.
///
pub fn reset(&mut self) -> Result<(), BusError> {
self.bus.load(0, &LO_ROM)?;
self.bus.load(0x10000, &HI_ROM)?;
@ -29,30 +35,44 @@ impl Dmd {
Ok(())
}
///
/// Return a view of the terminal's Video Memory
///
pub fn video_ram(&self) -> Result<&[u8], BusError> {
self.bus.video_ram()
}
pub fn step(&mut self) {
self.cpu.step(&mut self.bus);
}
///
/// Return the contents of the CPU's Program Counter.
///
pub fn get_pc(&self) -> u32 {
self.cpu.get_pc()
}
///
/// Return the contents of the CPU's Argument Pointer.
///
pub fn get_ap(&self) -> u32 {
self.cpu.get_ap()
}
///
/// Return the contents of the CPU's Processor Status Word.
///
pub fn get_psw(&self) -> u32 {
self.cpu.get_psw()
}
///
/// Return the contents of a CPU register (0-15)
///
pub fn get_register(&self, reg: u8) -> u32 {
self.cpu.r[reg as usize]
self.cpu.r[(reg & 0xf) as usize]
}
///
/// Read a word from the terminal's memory. NB: Address must be word aligned.
///
pub fn read(&mut self, addr: usize) -> Option<u32> {
match self.bus.read_word(addr, AccessCode::AddressFetch) {
Ok(d) => Some(d),
@ -60,30 +80,66 @@ impl Dmd {
}
}
///
/// Step the terminal's CPU one time, allowing the CPU's internal error and
/// exception handling to take care of all error / exception types.
///
pub fn step(&mut self) {
self.cpu.step(&mut self.bus);
}
///
/// Step the terminal's CPU one time, returning any error that may have occured.
/// Useful for debugging.
///
pub fn step_with_error(&mut self) -> Result<(), CpuError> {
self.cpu.step_with_error(&mut self.bus)
}
///
/// Poll for a character to transmit from the terminal to the host.
///
pub fn tx_poll(&mut self) -> Option<u8> {
self.bus.tx_poll()
}
///
/// Receive a character from the host to the terminal.
///
pub fn rx_char(&mut self, character: u8) -> Result<(), DuartError> {
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 keyboard(&mut self, keycode: u8) {
self.bus.keyboard(keycode);
}
///
/// Register a new mouse position with the terminal.
///
pub fn mouse_move(&mut self, x: u16, y: u16) {
self.bus.mouse_move(x, y);
}
///
/// Register a "mouse down" event with the terminal.
pub fn mouse_down(&mut self, button: u8) {
self.bus.mouse_down(button);
}
///
/// Register a "mouse up" event with the terminal.
///
pub fn mouse_up(&mut self, button: u8) {
self.bus.mouse_up(button);
}
@ -93,11 +149,9 @@ impl Dmd {
mod tests {
use crate::dmd::Dmd;
fn tx_callback(_char: u8) {}
#[test]
fn creates_dmd() {
let mut dmd = Dmd::new(tx_callback);
let mut dmd = Dmd::new();
dmd.reset().unwrap();
}
}

View File

@ -1,13 +1,15 @@
use crate::bus::AccessCode;
use crate::bus::Device;
use crate::err::BusError;
use crate::err::DuartError;
use std::fmt::Debug;
use std::fmt::Error;
use std::fmt::Formatter;
use std::ops::Range;
use std::time::Duration;
use std::time::Instant;
use crate::err::DuartError;
use std::collections::VecDeque;
const START_ADDR: usize = 0x200000;
const END_ADDR: usize = 0x2000040;
@ -112,11 +114,11 @@ pub struct Duart {
imr: u8,
ivec: u8,
last_vblank: Instant,
tx_callback: Option<Box<FnMut(u8) + Send + Sync>>,
tx_queue: VecDeque<u8>,
}
impl Duart {
pub fn new<CB: 'static + FnMut(u8) + Send + Sync>(tx_callback: CB) -> Duart {
pub fn new() -> Duart {
Duart {
ports: [
Port {
@ -153,7 +155,7 @@ impl Duart {
imr: 0,
ivec: 0,
last_vblank: Instant::now(),
tx_callback: Some(Box::new(tx_callback)),
tx_queue: VecDeque::new(),
}
}
@ -193,22 +195,11 @@ impl Duart {
self.istat |= ISTS_RAI;
self.ivec |= RX_INT;
} else {
match &mut self.tx_callback {
Some(cb) => (cb)(c),
None => {}
};
self.tx_queue.push_front(c);
}
}
}
pub fn handle_keyboard(&mut self, val: u8) {
let mut ctx = &mut self.ports[PORT_1];
ctx.rx_data = val;
ctx.stat |= STS_RXR;
self.istat |= ISTS_RBI;
self.ivec |= KEYBOARD_INT;
}
pub fn vertical_blank(&mut self) {
self.ivec |= MOUSE_BLANK_INT;
self.ipcr |= 0x40;
@ -221,6 +212,10 @@ impl Duart {
}
}
pub fn tx_poll(&mut self) -> Option<u8> {
self.tx_queue.pop_back()
}
pub fn mouse_down(&mut self, button: u8) {
self.ipcr = 0;
self.inprt |= 0xb;
@ -268,6 +263,31 @@ impl Duart {
return (ctx.stat & STS_RXR) != 0;
}
pub fn rx_keyboard(&mut self, c: u8) -> Result<(), DuartError> {
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 {
ctx.next_rx = Instant::now() + ctx.char_delay;
ctx.rx_pending = true;
Err(DuartError::ReceiverNotReady)
}
}
pub fn rx_char(&mut self, c: u8) -> Result<(), DuartError> {
let mut ctx = &mut self.ports[PORT_0];