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 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
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;
|
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(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
src/vm.rs
25
src/vm.rs
|
@ -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 --
|
||||||
|
|
Loading…
Reference in a new issue