Reworking devices to set up for a simple buff device

This commit is contained in:
Reid 'arrdem' McKenzie 2022-12-24 01:09:45 -07:00
parent be87f13956
commit fb2c4774eb
6 changed files with 200 additions and 56 deletions

View file

@ -1,19 +1,39 @@
pub mod buff;
pub mod console; pub mod console;
pub mod null; pub mod null;
pub mod system; pub mod system;
use crate::vm::Uxn; use crate::vm::Uxn;
#[derive(Debug, PartialEq)]
pub enum DeviceError {
AddressOverflow,
PortTypeError(u8, String),
}
pub trait Device: std::fmt::Debug { pub trait Device: std::fmt::Debug {
/** /**
* Callbacks used by UXN to do input from a "device". * Callbacks used by UXN to do input from a "device".
*/ */
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8; fn dei1(&mut self, vm: &mut Uxn, port: u8) -> Result<u8, DeviceError>;
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16;
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> Result<u16, DeviceError> {
return Ok(((self.dei1(vm, port)? as u16) << 8) + self.dei1(vm, port + 1)? as u16);
}
/** /**
* Callback used by UXN to do output through a "device". * Callback used by UXN to do output through a "device".
*/ */
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8); fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError>;
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16);
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) -> Result<(), DeviceError> {
let slot = port & 0xF;
if slot < 0xF {
return Err(DeviceError::AddressOverflow);
}
let [high, low] = val.to_be_bytes();
self.deo1(vm, slot, high)?;
self.deo1(vm, slot + 1, low)?;
Ok(())
}
} }

36
src/device/buff.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::device::Device;
use crate::device::DeviceError;
use crate::vm::Uxn;
/**
* The null device does nothing when reading or writing.
*/
#[derive(Debug)]
pub struct BuffDevice {
buffer: [u8; 16],
}
impl BuffDevice {
pub fn new() -> BuffDevice {
BuffDevice { buffer: [0; 16] }
}
}
impl Device for BuffDevice {
fn dei1(&mut self, _vm: &mut Uxn, port: u8) -> Result<u8, DeviceError> {
let slot = (port & 0xF) as usize;
Ok(self.buffer[slot])
}
fn dei2(&mut self, _vm: &mut Uxn, port: u8) -> Result<u16, DeviceError> {
let slot = (port & 0xF) as usize;
Ok(0)
}
fn deo1(&mut self, _vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError> {
let slot = (port & 0xF) as usize;
Ok(())
}
fn deo2(&mut self, _vm: &mut Uxn, port: u8, val: u16) -> Result<(), DeviceError> {
let slot = (port & 0xF) as usize;
Ok(())
}
}

View file

@ -1,4 +1,4 @@
use crate::device::Device; use crate::device::{Device, DeviceError};
use crate::vm::Uxn; use crate::vm::Uxn;
/** /**
@ -40,21 +40,33 @@ impl Device for ConsoleDevice {
* If inidx < inbuffer.size, we want to return the next character out of the * If inidx < inbuffer.size, we want to return the next character out of the
* inbuffer. * inbuffer.
*/ */
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8 { fn dei1(&mut self, vm: &mut Uxn, port: u8) -> Result<u8, DeviceError> {
let slot = port & 0xF; let slot = port & 0xF;
match slot { match slot {
0x2 => 0, 0x2 => Ok(0),
_ => 0, _ => Ok(0),
} }
} }
// DEI2 is not supported on the terminal device // DEI2 is not supported on the terminal device
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16 { fn dei2(&mut self, vm: &mut Uxn, port: u8) -> Result<u16, DeviceError> {
0 Err(DeviceError::PortTypeError(
port,
"double reads from the console are not supported".into(),
))
} }
/** /**
* Callback used by UXN to do output through a "device". * Callback used by UXN to do output through a "device".
*/ */
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {} fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError> {
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {} Ok(())
}
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) -> Result<(), DeviceError> {
Err(DeviceError::PortTypeError(
port,
"double writes to the console are not supported".into(),
))
}
} }

View file

@ -1,4 +1,4 @@
use crate::device::Device; use crate::device::{Device, DeviceError};
use crate::vm::Uxn; use crate::vm::Uxn;
/** /**
@ -14,12 +14,17 @@ impl NullDevice {
} }
impl Device for NullDevice { impl Device for NullDevice {
fn dei1(&mut self, _vm: &mut Uxn, _port: u8) -> u8 { fn dei1(&mut self, _vm: &mut Uxn, _port: u8) -> Result<u8, DeviceError> {
0 Ok(0)
} }
fn dei2(&mut self, _vm: &mut Uxn, _port: u8) -> u16 { fn dei2(&mut self, _vm: &mut Uxn, _port: u8) -> Result<u16, DeviceError> {
0 Ok(0)
}
fn deo1(&mut self, _vm: &mut Uxn, _port: u8, _val: u8) -> Result<(), DeviceError> {
Ok(())
}
fn deo2(&mut self, _vm: &mut Uxn, _port: u8, _val: u16) -> Result<(), DeviceError> {
Ok(())
} }
fn deo1(&mut self, _vm: &mut Uxn, _port: u8, _val: u8) {}
fn deo2(&mut self, _vm: &mut Uxn, _port: u8, _val: u16) {}
} }

View file

@ -1,4 +1,4 @@
use crate::device::Device; use crate::device::{Device, DeviceError};
use crate::vm::Uxn; use crate::vm::Uxn;
/** /**
@ -6,48 +6,112 @@ use crate::vm::Uxn;
*/ */
#[derive(Debug)] #[derive(Debug)]
pub struct SystemDevice { pub struct SystemDevice {
buffer: [u8; 0xF], vector: u16, // 0x0
red: u16, // 0x8
green: u16, // 0xA
blue: u16, // 0xC
debug: u8, // 0xE
state: u8, // 0xF
} }
impl SystemDevice { impl SystemDevice {
pub fn new() -> SystemDevice { pub fn new() -> SystemDevice {
SystemDevice { buffer: [0; 0xF] } SystemDevice {
vector: 0,
red: 0,
blue: 0,
green: 0,
debug: 0,
state: 0,
}
} }
} }
impl Device for SystemDevice { impl Device for SystemDevice {
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8 { fn dei1(&mut self, vm: &mut Uxn, port: u8) -> Result<u8, DeviceError> {
let slot = port & 0xF; let slot = port & 0xF;
match slot { Ok(match slot {
// High and low words of the vector
0x0 => (self.vector >> 8) as u8,
0x1 => (self.vector & 0xFF) as u8,
// The stack ports (somewhat nonstandard)
0x2 => vm.wst.borrow().idx(), 0x2 => vm.wst.borrow().idx(),
0x3 => vm.rst.borrow().idx(), 0x3 => vm.rst.borrow().idx(),
x => self.buffer[x as usize],
} // Red
0x8 => (self.red >> 8) as u8,
0x9 => (self.red & 0xFF) as u8,
// Green
0xa => (self.green >> 8) as u8,
0xb => (self.green & 0xFF) as u8,
// Blue
0xc => (self.blue >> 8) as u8,
0xd => (self.blue & 0xFF) as u8,
// The state ports
0xe => self.debug,
0xf => self.state,
_ => unreachable!(),
})
} }
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16 {
return ((self.dei1(vm, port) as u16) << 8) + self.dei1(vm, port + 1) as u16; fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError> {
}
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {
let slot = port & 0xF; let slot = port & 0xF;
match slot { match slot {
// Note: This VM does not support mutating the stack pointers. // High and low words of the vector
// This operation is not well defined upstream 0x0 => {
0x2 => panic!("Invoked unsupported mutation of the data stack pointer"), self.vector = self.vector & 0xFF | ((val as u16) << 8);
0x3 => panic!("Invoked unsupported mutation of the return stack pointer"),
x => self.buffer[x as usize] = val,
}
}
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {
let slot = port & 0xF;
assert!(slot < 0xF, "Double-write beyond the end of the device");
match slot {
0x2 => panic!("The data stack is single-width, panic on spurious double-write"),
0x3 => panic!("The return stack is single-width, panic on spurious double-write"),
x => {
let [high, low] = val.to_be_bytes();
self.deo1(vm, x, high);
self.deo1(vm, x + 1, low)
} }
0x1 => {
self.vector = self.vector & 0xFF00 | (val as u16);
}
// Note: This VM does not support mutating the stack pointers.
// It probably should, but it doesn't.
0x2 => {
return Err(DeviceError::PortTypeError(
port,
"Invoked unsupported mutation of the data stack pointer".into(),
))
}
0x3 => {
return Err(DeviceError::PortTypeError(
port,
"Invoked unsupported mutation of the return stack pointer".into(),
))
}
// Red
0x8 => {
self.red = self.red & 0xFF | ((val as u16) << 8);
}
0x9 => {
self.red = self.red & 0xFF00 | (val as u16);
}
// Green
0xa => {
self.green = self.green & 0xFF | ((val as u16) << 8);
}
0xb => {
self.green = self.green & 0xFF00 | (val as u16);
}
// Blue
0xc => {
self.blue = self.blue & 0xFF | ((val as u16) << 8);
}
0xd => {
self.blue = self.blue & 0xFF00 | (val as u16);
}
// The state ports
0xe => self.debug = val,
0xf => self.state = val,
_ => unreachable!(),
} }
Ok(())
} }
} }

View file

@ -15,6 +15,7 @@ use crate::stack::*;
pub enum UxnError { pub enum UxnError {
StackError(StackError), StackError(StackError),
MemoryError(MemoryError), MemoryError(MemoryError),
DeviceError(DeviceError),
ExecutionLimit(u16), ExecutionLimit(u16),
Break, Break,
ArithmeticOverflow, ArithmeticOverflow,
@ -34,6 +35,12 @@ impl From<MemoryError> for UxnError {
} }
} }
impl From<DeviceError> for UxnError {
fn from(e: DeviceError) -> UxnError {
UxnError::DeviceError(e)
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Uxn { pub struct Uxn {
pub memory: Rc<RefCell<TrivialMemory>>, pub memory: Rc<RefCell<TrivialMemory>>,
@ -83,27 +90,27 @@ impl Uxn {
} }
pub fn is_halted(&mut self) -> bool { pub fn is_halted(&mut self) -> bool {
self.dei1(0x0f) != 0 self.dei1(0x0f).unwrap() != 0
} }
fn device(&self, port: u8) -> Rc<RefCell<dyn Device>> { fn device(&self, port: u8) -> Rc<RefCell<dyn Device>> {
Rc::clone(&self.devices[(port & 0xF0 >> 4) as usize]) Rc::clone(&self.devices[(port & 0xF0 >> 4) as usize])
} }
pub fn dei1(&mut self, port: u8) -> u8 { pub fn dei1(&mut self, port: u8) -> Result<u8, DeviceError> {
self.device(port).borrow_mut().dei1(self, port) self.device(port).borrow_mut().dei1(self, port)
} }
pub fn dei2(&mut self, port: u8) -> u16 { pub fn dei2(&mut self, port: u8) -> Result<u16, DeviceError> {
self.device(port).borrow_mut().dei2(self, port) self.device(port).borrow_mut().dei2(self, port)
} }
pub fn deo1(&mut self, port: u8, val: u8) { pub fn deo1(&mut self, port: u8, val: u8) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo1(self, port, val); self.device(port).borrow_mut().deo1(self, port, val)
} }
pub fn deo2(&mut self, port: u8, val: u16) { pub fn deo2(&mut self, port: u8, val: u16) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo2(self, port, val); self.device(port).borrow_mut().deo2(self, port, val)
} }
pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> { pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> {
@ -359,13 +366,13 @@ impl Uxn {
// DEI port8 -- a8 // DEI port8 -- a8
let mut wst = wst.borrow_mut(); let mut wst = wst.borrow_mut();
let port = wst.pop1()?; let port = wst.pop1()?;
wst.push1(self.dei1(port))?; wst.push1(self.dei1(port)?)?;
} }
(_, _, 1, Icode::DEI) => { (_, _, 1, Icode::DEI) => {
// DEI2 port8 -- a16 // DEI2 port8 -- a16
let mut wst = wst.borrow_mut(); let mut wst = wst.borrow_mut();
let port = wst.pop1()?; let port = wst.pop1()?;
wst.push2(self.dei2(port))?; wst.push2(self.dei2(port)?)?;
} }
(_, _, 0, Icode::DEO) => { (_, _, 0, Icode::DEO) => {
// DEO1 a8 port8 -- // DEO1 a8 port8 --