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 null;
pub mod system;
use crate::vm::Uxn;
#[derive(Debug, PartialEq)]
pub enum DeviceError {
AddressOverflow,
PortTypeError(u8, String),
}
pub trait Device: std::fmt::Debug {
/**
* Callbacks used by UXN to do input from a "device".
*/
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8;
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16;
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> Result<u8, DeviceError>;
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".
*/
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8);
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16);
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError>;
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;
/**
@ -40,21 +40,33 @@ impl Device for ConsoleDevice {
* If inidx < inbuffer.size, we want to return the next character out of the
* 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;
match slot {
0x2 => 0,
_ => 0,
0x2 => Ok(0),
_ => Ok(0),
}
}
// DEI2 is not supported on the terminal device
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16 {
0
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> Result<u16, DeviceError> {
Err(DeviceError::PortTypeError(
port,
"double reads from the console are not supported".into(),
))
}
/**
* Callback used by UXN to do output through a "device".
*/
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {}
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {}
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> {
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;
/**
@ -14,12 +14,17 @@ impl NullDevice {
}
impl Device for NullDevice {
fn dei1(&mut self, _vm: &mut Uxn, _port: u8) -> u8 {
0
fn dei1(&mut self, _vm: &mut Uxn, _port: u8) -> Result<u8, DeviceError> {
Ok(0)
}
fn dei2(&mut self, _vm: &mut Uxn, _port: u8) -> u16 {
0
fn dei2(&mut self, _vm: &mut Uxn, _port: u8) -> Result<u16, DeviceError> {
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;
/**
@ -6,48 +6,112 @@ use crate::vm::Uxn;
*/
#[derive(Debug)]
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 {
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 {
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;
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(),
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) {
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) -> Result<(), DeviceError> {
let slot = port & 0xF;
match slot {
// High and low words of the vector
0x0 => {
self.vector = self.vector & 0xFF | ((val as u16) << 8);
}
0x1 => {
self.vector = self.vector & 0xFF00 | (val as u16);
}
// Note: This VM does not support mutating the stack pointers.
// This operation is not well defined upstream
0x2 => panic!("Invoked unsupported mutation of the data stack pointer"),
0x3 => panic!("Invoked unsupported mutation of the return stack pointer"),
x => self.buffer[x as usize] = val,
// 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(),
))
}
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)
// 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 {
StackError(StackError),
MemoryError(MemoryError),
DeviceError(DeviceError),
ExecutionLimit(u16),
Break,
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)]
pub struct Uxn {
pub memory: Rc<RefCell<TrivialMemory>>,
@ -83,27 +90,27 @@ impl Uxn {
}
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>> {
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)
}
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)
}
pub fn deo1(&mut self, port: u8, val: u8) {
self.device(port).borrow_mut().deo1(self, port, val);
pub fn deo1(&mut self, port: u8, val: u8) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo1(self, port, val)
}
pub fn deo2(&mut self, port: u8, val: u16) {
self.device(port).borrow_mut().deo2(self, port, val);
pub fn deo2(&mut self, port: u8, val: u16) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo2(self, port, val)
}
pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> {
@ -359,13 +366,13 @@ impl Uxn {
// DEI port8 -- a8
let mut wst = wst.borrow_mut();
let port = wst.pop1()?;
wst.push1(self.dei1(port))?;
wst.push1(self.dei1(port)?)?;
}
(_, _, 1, Icode::DEI) => {
// DEI2 port8 -- a16
let mut wst = wst.borrow_mut();
let port = wst.pop1()?;
wst.push2(self.dei2(port))?;
wst.push2(self.dei2(port)?)?;
}
(_, _, 0, Icode::DEO) => {
// DEO1 a8 port8 --