Reworking devices to set up for a simple buff device
This commit is contained in:
parent
be87f13956
commit
fb2c4774eb
6 changed files with 200 additions and 56 deletions
|
@ -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
36
src/device/buff.rs
Normal 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(())
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
25
src/vm.rs
25
src/vm.rs
|
@ -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 --
|
||||
|
|
Loading…
Reference in a new issue