Create a PopStack helper for keep mode
This commit is contained in:
parent
b7ade42661
commit
e74e2458ef
5 changed files with 370 additions and 230 deletions
229
src/stack.rs
229
src/stack.rs
|
@ -1,8 +1,11 @@
|
||||||
|
pub mod arrs;
|
||||||
|
pub mod pop;
|
||||||
|
pub mod wal;
|
||||||
|
|
||||||
use std;
|
use std;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum StackError {
|
pub enum StackError {
|
||||||
StackOverflow,
|
StackOverflow,
|
||||||
StackUnderflow,
|
StackUnderflow,
|
||||||
|
@ -29,225 +32,3 @@ pub trait Stack: std::fmt::Debug {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ArrayStack {
|
|
||||||
buff: [u8; 0xFF],
|
|
||||||
_idx: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ArrayStack {
|
|
||||||
pub fn new() -> ArrayStack {
|
|
||||||
ArrayStack {
|
|
||||||
buff: [0; 0xFF],
|
|
||||||
_idx: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stack for ArrayStack {
|
|
||||||
fn idx(&self) -> u8 {
|
|
||||||
return self._idx;
|
|
||||||
}
|
|
||||||
fn push1(&mut self, val: u8) -> Result<(), StackError> {
|
|
||||||
if self._idx == 255 {
|
|
||||||
Err(StackError::StackOverflow)
|
|
||||||
} else {
|
|
||||||
self.buff[self._idx as usize] = val;
|
|
||||||
self._idx += 1;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push2(&mut self, val: u16) -> Result<(), StackError> {
|
|
||||||
if self._idx > 254 {
|
|
||||||
Err(StackError::StackOverflow)
|
|
||||||
} else {
|
|
||||||
val.to_be_bytes().map(|x| self.push1(x).ok());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get1(&self, _idx: u8) -> Result<u8, StackError> {
|
|
||||||
Ok(self.buff[_idx as usize])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek1(&self) -> Result<u8, StackError> {
|
|
||||||
if self._idx == 0 {
|
|
||||||
Err(StackError::StackUnderflow)
|
|
||||||
} else {
|
|
||||||
Ok(self.get1(self._idx - 1)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop1(&mut self) -> Result<u8, StackError> {
|
|
||||||
let val = self.peek1()?;
|
|
||||||
self._idx -= 1;
|
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get2(&self, _idx: u8) -> Result<u16, StackError> {
|
|
||||||
if _idx == 255 {
|
|
||||||
Err(StackError::StackOverflow)
|
|
||||||
} else {
|
|
||||||
let high = self.buff[_idx as usize] as u16;
|
|
||||||
let low = self.buff[(_idx + 1) as usize] as u16;
|
|
||||||
println!("_idx {}; high {:#02x}; low {:#02x};", _idx, high, low);
|
|
||||||
Ok((high << 8) + low)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peek2(&self) -> Result<u16, StackError> {
|
|
||||||
if self._idx < 2 {
|
|
||||||
Err(StackError::StackUnderflow)
|
|
||||||
} else {
|
|
||||||
self.get2(self._idx - 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop2(&mut self) -> Result<u16, StackError> {
|
|
||||||
let val = self.peek2()?;
|
|
||||||
self._idx -= 2;
|
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod arraystack_test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_push_pop() {
|
|
||||||
let mut s = ArrayStack::new();
|
|
||||||
match s.push1(0xFF) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 1);
|
|
||||||
|
|
||||||
match s.peek1() {
|
|
||||||
Ok(val) => assert_eq!(val, 0xFF),
|
|
||||||
Err(_) => assert!(false, "popping stack of size 1 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 1);
|
|
||||||
|
|
||||||
match s.pop1() {
|
|
||||||
Ok(val) => assert_eq!(val, 0xFF),
|
|
||||||
Err(_) => assert!(false, "popping stack of size 1 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_push2_pop2() {
|
|
||||||
let mut s = ArrayStack::new();
|
|
||||||
match s.push2(0xFF00) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 2);
|
|
||||||
|
|
||||||
match s.peek2() {
|
|
||||||
Ok(val) => assert_eq!(val, 0xFF00),
|
|
||||||
Err(_) => assert!(false, "peek2 stack of size 2 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 2);
|
|
||||||
|
|
||||||
match s.pop2() {
|
|
||||||
Ok(val) => assert_eq!(val, 0xFF00),
|
|
||||||
Err(_) => assert!(false, "peek2 stack of size 2 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_push2_pop1_pop1() {
|
|
||||||
let mut s = ArrayStack::new();
|
|
||||||
match s.push2(0xFF00) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 2);
|
|
||||||
|
|
||||||
match s.pop1() {
|
|
||||||
Ok(val) => assert_eq!(val, 0x00),
|
|
||||||
Err(_) => assert!(false, "peek1 stack of size 2 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 1);
|
|
||||||
|
|
||||||
match s.pop1() {
|
|
||||||
Ok(val) => assert_eq!(val, 0xFF),
|
|
||||||
Err(_) => assert!(false, "peek1 stack of size 2 errored"),
|
|
||||||
}
|
|
||||||
assert_eq!(s._idx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pop_empty() {
|
|
||||||
let mut s = ArrayStack::new();
|
|
||||||
match s.pop1() {
|
|
||||||
Ok(_) => assert!(false, "popping stack of size 0 succeeded"),
|
|
||||||
Err(StackError::StackUnderflow) => (),
|
|
||||||
Err(_) => assert!(false, "poppoing stack of size 0 didn't underflow"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum StackOp {
|
|
||||||
Push1(u8),
|
|
||||||
Push2(u16),
|
|
||||||
Pop1(),
|
|
||||||
Pop2(),
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A writeahead log wrapping a read copy of another stack.
|
|
||||||
* Can be committed
|
|
||||||
*/
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StackWAL {
|
|
||||||
log: Vec<StackOp>,
|
|
||||||
_idx: u8,
|
|
||||||
wrapped: Rc<RefCell<dyn Stack>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StackWAL {
|
|
||||||
pub fn new(wrapped: Rc<RefCell<dyn Stack>>) -> StackWAL {
|
|
||||||
return StackWAL {
|
|
||||||
log: vec![],
|
|
||||||
_idx: wrapped.borrow().idx(),
|
|
||||||
wrapped: wrapped.clone(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stack for StackWAL {
|
|
||||||
fn idx(&self) -> u8 {
|
|
||||||
return self._idx;
|
|
||||||
}
|
|
||||||
fn push1(&mut self, val: u8) -> Result<(), StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn push2(&mut self, val: u16) -> Result<(), StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get1(&self, idx: u8) -> Result<u8, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn get2(&self, idx: u8) -> Result<u16, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn peek1(&self) -> Result<u8, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn peek2(&self) -> Result<u16, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn pop1(&mut self) -> Result<u8, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn pop2(&mut self) -> Result<u16, StackError> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
171
src/stack/arrs.rs
Normal file
171
src/stack/arrs.rs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
use super::{Stack, StackError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ArrayStack {
|
||||||
|
buff: [u8; 0xFF],
|
||||||
|
_idx: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayStack {
|
||||||
|
pub fn new() -> ArrayStack {
|
||||||
|
ArrayStack {
|
||||||
|
buff: [0; 0xFF],
|
||||||
|
_idx: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn of1(contents: &[u8]) -> ArrayStack {
|
||||||
|
let mut s = ArrayStack::new();
|
||||||
|
for it in contents.iter() {
|
||||||
|
s.push1(*it).unwrap();
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack for ArrayStack {
|
||||||
|
fn idx(&self) -> u8 {
|
||||||
|
return self._idx;
|
||||||
|
}
|
||||||
|
fn push1(&mut self, val: u8) -> Result<(), StackError> {
|
||||||
|
if self._idx == 255 {
|
||||||
|
Err(StackError::StackOverflow)
|
||||||
|
} else {
|
||||||
|
self.buff[self._idx as usize] = val;
|
||||||
|
self._idx += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push2(&mut self, val: u16) -> Result<(), StackError> {
|
||||||
|
if self._idx > 254 {
|
||||||
|
Err(StackError::StackOverflow)
|
||||||
|
} else {
|
||||||
|
val.to_be_bytes().map(|x| self.push1(x).ok());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get1(&self, _idx: u8) -> Result<u8, StackError> {
|
||||||
|
Ok(self.buff[_idx as usize])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek1(&self) -> Result<u8, StackError> {
|
||||||
|
if self._idx == 0 {
|
||||||
|
Err(StackError::StackUnderflow)
|
||||||
|
} else {
|
||||||
|
Ok(self.get1(self._idx - 1)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop1(&mut self) -> Result<u8, StackError> {
|
||||||
|
let val = self.peek1()?;
|
||||||
|
self._idx -= 1;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get2(&self, _idx: u8) -> Result<u16, StackError> {
|
||||||
|
if _idx == 255 {
|
||||||
|
Err(StackError::StackOverflow)
|
||||||
|
} else {
|
||||||
|
let high = self.buff[_idx as usize] as u16;
|
||||||
|
let low = self.buff[(_idx + 1) as usize] as u16;
|
||||||
|
println!("_idx {}; high {:#02x}; low {:#02x};", _idx, high, low);
|
||||||
|
Ok((high << 8) + low)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek2(&self) -> Result<u16, StackError> {
|
||||||
|
if self._idx < 2 {
|
||||||
|
Err(StackError::StackUnderflow)
|
||||||
|
} else {
|
||||||
|
self.get2(self._idx - 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop2(&mut self) -> Result<u16, StackError> {
|
||||||
|
let val = self.peek2()?;
|
||||||
|
self._idx -= 2;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod arrs_test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_push_pop() {
|
||||||
|
let mut s = ArrayStack::new();
|
||||||
|
match s.push1(0xFF) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 1);
|
||||||
|
|
||||||
|
match s.peek1() {
|
||||||
|
Ok(val) => assert_eq!(val, 0xFF),
|
||||||
|
Err(_) => assert!(false, "popping stack of size 1 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 1);
|
||||||
|
|
||||||
|
match s.pop1() {
|
||||||
|
Ok(val) => assert_eq!(val, 0xFF),
|
||||||
|
Err(_) => assert!(false, "popping stack of size 1 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_push2_pop2() {
|
||||||
|
let mut s = ArrayStack::new();
|
||||||
|
match s.push2(0xFF00) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 2);
|
||||||
|
|
||||||
|
match s.peek2() {
|
||||||
|
Ok(val) => assert_eq!(val, 0xFF00),
|
||||||
|
Err(_) => assert!(false, "peek2 stack of size 2 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 2);
|
||||||
|
|
||||||
|
match s.pop2() {
|
||||||
|
Ok(val) => assert_eq!(val, 0xFF00),
|
||||||
|
Err(_) => assert!(false, "peek2 stack of size 2 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_push2_pop1_pop1() {
|
||||||
|
let mut s = ArrayStack::new();
|
||||||
|
match s.push2(0xFF00) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(_) => assert!(false, "pushing stack of size 0 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 2);
|
||||||
|
|
||||||
|
match s.pop1() {
|
||||||
|
Ok(val) => assert_eq!(val, 0x00),
|
||||||
|
Err(_) => assert!(false, "peek1 stack of size 2 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 1);
|
||||||
|
|
||||||
|
match s.pop1() {
|
||||||
|
Ok(val) => assert_eq!(val, 0xFF),
|
||||||
|
Err(_) => assert!(false, "peek1 stack of size 2 errored"),
|
||||||
|
}
|
||||||
|
assert_eq!(s._idx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pop_empty() {
|
||||||
|
let mut s = ArrayStack::new();
|
||||||
|
match s.pop1() {
|
||||||
|
Ok(_) => assert!(false, "popping stack of size 0 succeeded"),
|
||||||
|
Err(StackError::StackUnderflow) => (),
|
||||||
|
Err(_) => assert!(false, "poppoing stack of size 0 didn't underflow"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
src/stack/pop.rs
Normal file
117
src/stack/pop.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::stack::{Stack, StackError};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy stack which can be popped from without impacting the underlying stack.
|
||||||
|
*
|
||||||
|
* This is not a generally useful structure; it exists to enable the
|
||||||
|
* implementation of UXN's 'keep' bit, which causes all pops on either stack to
|
||||||
|
* occur as reads without modification of the stack pointer.
|
||||||
|
*
|
||||||
|
* In this mode POP is a no-op, and (LIT a DUPk) could produce (a a a).
|
||||||
|
*/
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PopStack {
|
||||||
|
wrapped: Rc<RefCell<dyn Stack>>,
|
||||||
|
_idx: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopStack {
|
||||||
|
pub fn new(wrapped: Rc<RefCell<dyn Stack>>) -> PopStack {
|
||||||
|
PopStack {
|
||||||
|
wrapped: wrapped.clone(),
|
||||||
|
_idx: wrapped.borrow().idx(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack for PopStack {
|
||||||
|
fn idx(&self) -> u8 {
|
||||||
|
return self._idx;
|
||||||
|
}
|
||||||
|
fn push1(&mut self, val: u8) -> Result<(), StackError> {
|
||||||
|
self.wrapped.borrow_mut().push1(val)
|
||||||
|
}
|
||||||
|
fn push2(&mut self, val: u16) -> Result<(), StackError> {
|
||||||
|
self.wrapped.borrow_mut().push2(val)
|
||||||
|
}
|
||||||
|
fn get1(&self, idx: u8) -> Result<u8, StackError> {
|
||||||
|
self.wrapped.borrow().get1(idx)
|
||||||
|
}
|
||||||
|
fn get2(&self, idx: u8) -> Result<u16, StackError> {
|
||||||
|
self.wrapped.borrow().get2(idx)
|
||||||
|
}
|
||||||
|
fn peek1(&self) -> Result<u8, StackError> {
|
||||||
|
if self._idx == 0 {
|
||||||
|
return Err(StackError::StackUnderflow);
|
||||||
|
}
|
||||||
|
self.get1(self._idx - 1)
|
||||||
|
}
|
||||||
|
fn peek2(&self) -> Result<u16, StackError> {
|
||||||
|
if self._idx == 0 {
|
||||||
|
return Err(StackError::StackUnderflow);
|
||||||
|
}
|
||||||
|
self.get2(self._idx - 1)
|
||||||
|
}
|
||||||
|
fn pop1(&mut self) -> Result<u8, StackError> {
|
||||||
|
if self._idx == 0 {
|
||||||
|
return Err(StackError::StackUnderflow);
|
||||||
|
}
|
||||||
|
let val = self.get1(self._idx - 1)?;
|
||||||
|
self._idx -= 1;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
fn pop2(&mut self) -> Result<u16, StackError> {
|
||||||
|
if self._idx < 2 {
|
||||||
|
return Err(StackError::StackUnderflow);
|
||||||
|
}
|
||||||
|
let val = self.get2(self._idx - 1)?;
|
||||||
|
self._idx -= 2;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_ps {
|
||||||
|
use super::*;
|
||||||
|
use crate::stack::arrs::ArrayStack;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dummy_pop() {
|
||||||
|
// Set up the underlying stack...
|
||||||
|
let underlying: Rc<RefCell<dyn Stack>> =
|
||||||
|
Rc::new(RefCell::new(ArrayStack::of1(&[0xFF, 0xFE])));
|
||||||
|
|
||||||
|
// Set up the wrapper, which assumes it has exclusive access to the underlying stack
|
||||||
|
let wrapper: Rc<RefCell<dyn Stack>> =
|
||||||
|
Rc::new(RefCell::new(PopStack::new(underlying.clone())));
|
||||||
|
|
||||||
|
// We should be able to pop twice from the wrapper
|
||||||
|
assert_eq!(wrapper.borrow_mut().pop1().unwrap(), 0xFE);
|
||||||
|
assert_eq!(wrapper.borrow_mut().pop1().unwrap(), 0xFF);
|
||||||
|
|
||||||
|
// And the underlying stack should be unaffected
|
||||||
|
assert_eq!(underlying.borrow_mut().pop1().unwrap(), 0xFE);
|
||||||
|
assert_eq!(underlying.borrow_mut().pop1().unwrap(), 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pop_pushthrough() {
|
||||||
|
// Set up the underlying stack...
|
||||||
|
let underlying: Rc<RefCell<dyn Stack>> =
|
||||||
|
Rc::new(RefCell::new(ArrayStack::of1(&[0xFF, 0xFE])));
|
||||||
|
|
||||||
|
// Set up the wrapper, which assumes it has exclusive access to the underlying stack
|
||||||
|
let wrapper: Rc<RefCell<dyn Stack>> =
|
||||||
|
Rc::new(RefCell::new(PopStack::new(underlying.clone())));
|
||||||
|
|
||||||
|
// We should be able to pop the wrapper without impacting the underlying
|
||||||
|
assert_eq!(wrapper.borrow_mut().pop1().unwrap(), 0xFE);
|
||||||
|
assert_eq!(underlying.borrow_mut().peek1().unwrap(), 0xFE);
|
||||||
|
|
||||||
|
// But if we push, the underlying should see the change
|
||||||
|
wrapper.borrow_mut().push1(0xFD).unwrap();
|
||||||
|
assert_eq!(underlying.borrow_mut().peek1().unwrap(), 0xFD);
|
||||||
|
}
|
||||||
|
}
|
62
src/stack/wal.rs
Normal file
62
src/stack/wal.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use super::{Stack, StackError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum StackOp {
|
||||||
|
Push1(u8),
|
||||||
|
Push2(u16),
|
||||||
|
Pop1(),
|
||||||
|
Pop2(),
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A writeahead log wrapping a read copy of another stack.
|
||||||
|
* Can be committed
|
||||||
|
*/
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StackWAL {
|
||||||
|
log: Vec<StackOp>,
|
||||||
|
_idx: u8,
|
||||||
|
wrapped: Rc<RefCell<dyn Stack>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackWAL {
|
||||||
|
pub fn new(wrapped: Rc<RefCell<dyn Stack>>) -> StackWAL {
|
||||||
|
return StackWAL {
|
||||||
|
log: vec![],
|
||||||
|
_idx: wrapped.borrow().idx(),
|
||||||
|
wrapped: wrapped.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack for StackWAL {
|
||||||
|
fn idx(&self) -> u8 {
|
||||||
|
return self._idx;
|
||||||
|
}
|
||||||
|
fn push1(&mut self, val: u8) -> Result<(), StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn push2(&mut self, val: u16) -> Result<(), StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn get1(&self, idx: u8) -> Result<u8, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn get2(&self, idx: u8) -> Result<u16, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn peek1(&self) -> Result<u8, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn peek2(&self) -> Result<u16, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn pop1(&mut self) -> Result<u8, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn pop2(&mut self) -> Result<u16, StackError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
21
src/vm.rs
21
src/vm.rs
|
@ -6,6 +6,8 @@ use crate::device::null::NullDevice;
|
||||||
use crate::device::system::SystemDevice;
|
use crate::device::system::SystemDevice;
|
||||||
use crate::device::*;
|
use crate::device::*;
|
||||||
use crate::memory::*;
|
use crate::memory::*;
|
||||||
|
use crate::stack::arrs::ArrayStack;
|
||||||
|
use crate::stack::pop::PopStack;
|
||||||
use crate::stack::*;
|
use crate::stack::*;
|
||||||
|
|
||||||
pub enum UxnError {
|
pub enum UxnError {
|
||||||
|
@ -125,7 +127,7 @@ impl Uxn {
|
||||||
let icode: u8 = opcode & 0x1F;
|
let icode: u8 = opcode & 0x1F;
|
||||||
|
|
||||||
// Swizzle the stacks as needed
|
// Swizzle the stacks as needed
|
||||||
let [wst, rst] = {
|
let [wst, rst]: [Rc<RefCell<dyn Stack>>; 2] = {
|
||||||
if rflag == 0 {
|
if rflag == 0 {
|
||||||
[self.wst.clone(), self.rst.clone()]
|
[self.wst.clone(), self.rst.clone()]
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,6 +135,18 @@ impl Uxn {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Inject a PopStack shim to support the 'keep' bit as needed
|
||||||
|
let [wst, rst]: [Rc<RefCell<dyn Stack>>; 2] = {
|
||||||
|
if kflag == 0 {
|
||||||
|
[wst, rst]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
Rc::new(RefCell::new(PopStack::new(wst))),
|
||||||
|
Rc::new(RefCell::new(PopStack::new(rst))),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Some procedural abstractions over load/store sizes
|
// Some procedural abstractions over load/store sizes
|
||||||
let pop = |stack: Rc<RefCell<dyn Stack>>| -> Result<u16, StackError> {
|
let pop = |stack: Rc<RefCell<dyn Stack>>| -> Result<u16, StackError> {
|
||||||
if sflag == 1 {
|
if sflag == 1 {
|
||||||
|
@ -166,11 +180,6 @@ impl Uxn {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Just bail out of we get a keep flag, haven't figured out an abstraction for that yet.
|
|
||||||
if kflag != 0 {
|
|
||||||
panic!("Got unsupported keep-mode instruction");
|
|
||||||
}
|
|
||||||
|
|
||||||
match (kflag, rflag, sflag, icode) {
|
match (kflag, rflag, sflag, icode) {
|
||||||
(0, 0, 0, 0x00) => {
|
(0, 0, 0, 0x00) => {
|
||||||
// BRK
|
// BRK
|
||||||
|
|
Loading…
Reference in a new issue