From 4062ff4d09ae27c9e53c0ccdefffa29690c6113a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 11 Oct 2020 01:35:19 +0200 Subject: [PATCH] AMAZING NEW FEATURES ldn, bfio --- README.md | 81 +++++++++----- crsn/src/asm/parse/arg_parser.rs | 9 +- crsn/src/asm/parse/parse_data.rs | 29 +++-- crsn/src/builtin/defs.rs | 4 +- crsn/src/builtin/exec.rs | 11 ++ crsn/src/builtin/parse.rs | 14 ++- crsn_buf/src/defs.rs | 28 +++++ crsn_buf/src/exec.rs | 175 +++++++++++++++++++++---------- crsn_buf/src/lib.rs | 27 +++++ crsn_buf/src/parse.rs | 23 +++- crsn_screen/src/defs.rs | 3 +- crsn_screen/src/exec.rs | 12 +-- crsn_screen/src/lib.rs | 13 +++ crsn_screen/src/parse.rs | 16 ++- examples/buf_io.csn | 53 ++++++++++ examples/buf_multi.csn | 7 ++ examples/screen_bounce.csn | 4 +- 17 files changed, 400 insertions(+), 109 deletions(-) create mode 100644 examples/buf_io.csn create mode 100644 examples/buf_multi.csn diff --git a/README.md b/README.md index 9670641..b403e4b 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,8 @@ Jumping to a label is always safer than a manual skip. ## Built-in Instructions +...and pseudo-instructions + ``` ; Do nothing (nop) @@ -282,6 +284,15 @@ Jumping to a label is always safer than a manual skip. ; Stop execution (halt) +; Define a register alias. +; The alias is only valid in the current routine or in the root of the program. +; However, if the register is a global register, then the alias is valid everywhere. +(sym SYM REG) + +; Define a constant. These are valid in the whole program. +; Value must be known at compile time. +(def CONST VALUE) + ; Mark a jump target. (:LABEL) ; Numbered labels @@ -300,6 +311,21 @@ Jumping to a label is always safer than a manual skip. ; Skip backward or forward (s Rd) +; Copy value +(ld Wr Rd) + +; Copy N values. This is especially useful when used with stream handles or buffers. +(ldn Wr Rd Rd:count) + +; Swap values +(swap RW RW) + +; Store status flags to a register +(stf Wr) + +; Load status flags from a register +(ldf Rd) + ; Mark a routine entry point (call target). (routine PROC) (routine PROC/A) @@ -311,6 +337,11 @@ Jumping to a label is always safer than a manual skip. ; Exit the current routine with return values (ret Rd...) +; Generate a run-time fault with a debugger message +(fault) +(fault message) +(fault "message text") + ; Deny jumps, skips and run across this address, producing a run-time fault with a message. (barrier) (barrier message) @@ -321,32 +352,6 @@ Jumping to a label is always safer than a manual skip. ; The label can be a numeric or string label, its sole purpose is tying the two together. They must be unique in the program. (barrier-open LABEL) (barrier-close LABEL) - -; Generate a run-time fault with a debugger message -(fault) -(fault message) -(fault "message text") - -; Copy value -(ld Wr Rd) - -; Swap values -(swap RW RW) - -; Store status flags to a register -(stf Wr) - -; Load status flags from a register -(ldf Rd) - -; Define a register alias. -; The alias is only valid in the current routine or in the root of the program. -; However, if the register is a global register, then the alias is valid everywhere. -(sym SYM REG) - -; Define a constant. These are valid in the whole program. -; Value must be known at compile time. -(def CONST VALUE) ``` ## Arithmetic Module @@ -460,6 +465,24 @@ A buffer needs to be created using one of the init instructions: (mkbf Wr (Rd...)) ``` +Buffers can be as stacks or queues by reading and writing the handle (e.g. `(ld @buf 123)`). +The behavior of reads and writes is configurable per stack, and can be changed at any time. +The default mode is forward queue. + +This feature may be a bit confusing at first, but it is extremely powerful. +One consequence of this feature is that `(ld @buf @buf)` will move items from one end to the other +in one or the other direction (queue mode), or do nothing at all (stack mode). + +```list +; Set buffer IO mode +; Mode is one of: +; - BFIO_QUEUE (1) +; - BFIO_RQUEUE (2) +; - BFIO_STACK (3) +; - BFIO_RSTACK (4) +(bfio @Obj MODE) +``` + Primitive buffer ops (position is always 0-based) ```lisp @@ -540,9 +563,9 @@ such as animations. ; Set pixel color (sc-px X Y COLOR) -; Set screen option -; 1 ... auto-blit (blit automatically on pixel write when needed to achieve the target FPS) -; 2 ... frame rate +; Set screen option. Constants are pre-defined. +; SCREEN_AUTO_BLIT (1) ... auto-blit (blit automatically on pixel write when needed to achieve the target FPS) +; SCREEN_FPS (2) ... frame rate (sc-opt OPTION VALUE) ; Blit (render the pixel buffer). diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 1dfd62a..0ec8dff 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -2,9 +2,10 @@ use sexp::{Sexp, SourcePosition}; use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr}; use crate::asm::error::CrsnError; -use crate::asm::parse::parse_data::{parse_rd, parse_wr}; +use crate::asm::parse::parse_data::{parse_rd, parse_wr, parse_value}; use crate::asm::parse::ParserContext; use crate::asm::parse::sexp_expect::expect_string_atom; +use crate::asm::data::literal::Value; /// Utility for argument parsing #[derive(Debug)] @@ -85,6 +86,12 @@ impl<'a> TokenParser<'a> { Ok(esa) } + /// Get the next value entry + pub fn next_value(&mut self) -> Result<(Value, SourcePosition), CrsnError> { + let next = self.next_or_err()?; + parse_value(next, self.pcx) + } + /// Get the next entry as read location pub fn next_rd(&mut self) -> Result { let next = self.next_or_err()?; diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index dbc3ffe..b522f39 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -12,7 +12,7 @@ use crate::asm::patches::ErrWithPos; fn is_valid_identifier(name: &str) -> bool { // ascii symbols "!\"#$_&'()*+,-./:;<=>?@[\\]^_`{|}~" - const BLACKLIST : &str = "!\"#$&'()*+,-./:;<=>?@[\\]^`{|}~"; + const BLACKLIST : &str = "!\"#$&'()*+,./;<=>?@[\\]^`{|}~"; name != "_" && !name.starts_with(|c: char| c.is_ascii_digit() || BLACKLIST.contains(c)) && !name.contains(|c : char| c.is_whitespace() || BLACKLIST.contains(c)) @@ -161,27 +161,34 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result Result { +pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<(Value, SourcePosition), CrsnError> { match tok { - Sexp::Atom(Atom::I(val), _pos) => { - Ok(unsafe { std::mem::transmute(val) }) + Sexp::Atom(Atom::I(val), pos) => { + Ok((unsafe { std::mem::transmute(val) }, pos)) } - Sexp::Atom(Atom::U(val), _pos) => { - Ok(val) + Sexp::Atom(Atom::U(val), pos) => { + Ok((val, pos)) } - Sexp::Atom(Atom::C(val), _pos) => { - Ok(val as u64) + Sexp::Atom(Atom::C(val), pos) => { + Ok((val as u64, pos)) } Sexp::Atom(Atom::QS(_), pos) => { Err(CrsnError::Parse("quoted string not expected here".into(), pos)) } - Sexp::Atom(Atom::F(val), _) => { - Ok(unsafe { std::mem::transmute(val) }) + Sexp::Atom(Atom::F(val), pos) => { + Ok((unsafe { std::mem::transmute(val) }, pos)) } Sexp::Atom(Atom::S(s), pos) => { let pstate = pcx.state.borrow(); if let Some(val) = pstate.constants.get(&s) { - return Ok(*val); + return Ok((*val, pos)); + } + + /* extension constants */ + for p in pcx.parsers { + if let Some(val) = p.get_constant_value(&s) { + return Ok((val, pos)); + } } Err(CrsnError::Parse(format!("unknown constant: {}", s).into(), pos)) diff --git a/crsn/src/builtin/defs.rs b/crsn/src/builtin/defs.rs index 607add3..0724379 100644 --- a/crsn/src/builtin/defs.rs +++ b/crsn/src/builtin/defs.rs @@ -70,8 +70,10 @@ pub enum BuiltinOp { /// Deallocate an extension object. /// The object is released and the handle becomes invalid. Delete(RdObj), - /// Copy value + /// Move a value MoveValue { dst: Wr, src: Rd }, + /// Move N values + MoveMultipleValues { dst: Wr, src: Rd, count: Rd }, /// Swap two registers SwapValues { a: RdWr, b: RdWr }, /// Store runtime status to a register diff --git a/crsn/src/builtin/exec.rs b/crsn/src/builtin/exec.rs index f226863..4e174cf 100644 --- a/crsn/src/builtin/exec.rs +++ b/crsn/src/builtin/exec.rs @@ -109,6 +109,17 @@ impl OpTrait for BuiltinOp { state.update_status(val); state.write(dst, val)?; } + BuiltinOp::MoveMultipleValues { dst, src, count } => { + state.clear_status(); + let mut count = state.read(count)?; + let mut last = 0; + while count > 0 { + last = state.read(src)?; + state.write(dst, last)?; + count -= 1; + } + state.update_status(last); + } BuiltinOp::SwapValues { a, b } => { let aa = state.read(a)?; let bb = state.read(b)?; diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 9d04971..9e965aa 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -88,7 +88,7 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok "def" => { let (name, namepos) = parse_constant_name(args.next_or_err()?)?; - let value = parse_value(args.next_or_err()?, pcx)?; + let (value, _valuepos) = parse_value(args.next_or_err()?, pcx)?; let mut pstate = pcx.state.borrow_mut(); if pstate.constants.contains_key(&name) { @@ -189,6 +189,14 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok } } + "ldn" => { + BuiltinOp::MoveMultipleValues { + dst: args.next_wr()?, + src: args.next_rd()?, + count: args.next_rd()?, + } + } + "swap" => { BuiltinOp::SwapValues { a: args.next_rdwr()?, @@ -309,7 +317,8 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { } BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]), BuiltinOp::MoveValue { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]), - BuiltinOp::SwapValues { a, b } => sexp::list(&[A("swp"), A(a), A(b)]), + BuiltinOp::MoveMultipleValues { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]), + BuiltinOp::SwapValues { a, b } => sexp::list(&[A("swap"), A(a), A(b)]), BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]), BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]) } @@ -382,6 +391,7 @@ mod test { ("(ld r0 r0)", "(ld r0 r0)"), ("(ld r0 156)", "(ld r0 156)"), ("(ld _ -32767)", "(ld _ -32767)"), + ("(ldn _ @r0 7)", "(ldn _ @r0 7)"), ("(stf r0)", "(stf r0)"), ("(ldf r0)", "(ldf r0)"), ("(far :label)", "(far :label)"), diff --git a/crsn_buf/src/defs.rs b/crsn_buf/src/defs.rs index d397ad9..19973fa 100644 --- a/crsn_buf/src/defs.rs +++ b/crsn_buf/src/defs.rs @@ -1,4 +1,6 @@ use crsn::asm::data::{Rd, RdObj, Wr}; +use crsn::asm::data::literal::Value; +use crate::parse::{BUF_IOMODE_QUEUE, BUF_IOMODE_REVERSE_QUEUE, BUF_IOMODE_STACK, BUF_IOMODE_REVERSE_STACK}; #[derive(Clone, Debug, PartialEq)] pub enum BufValue { @@ -7,9 +9,35 @@ pub enum BufValue { Chars(String), } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum BufIoMode { + Queue, + ReverseQueue, + Stack, + ReverseStack, +} + +impl BufIoMode { + pub fn to_value(self) -> Value { + match self { + BufIoMode::Queue => BUF_IOMODE_QUEUE, + BufIoMode::ReverseQueue => BUF_IOMODE_REVERSE_QUEUE, + BufIoMode::Stack => BUF_IOMODE_STACK, + BufIoMode::ReverseStack => BUF_IOMODE_REVERSE_STACK, + } + } +} + +impl Default for BufIoMode { + fn default() -> Self { + Self::Queue + } +} + #[derive(Clone, Debug, PartialEq)] pub enum BufOps { New { dst: Wr, value: BufValue }, + SetIoMode { obj: RdObj, mode: BufIoMode }, // Elementary ops Read { dst: Wr, obj: RdObj, idx: Rd }, diff --git a/crsn_buf/src/exec.rs b/crsn_buf/src/exec.rs index 2a20a39..fae582e 100644 --- a/crsn_buf/src/exec.rs +++ b/crsn_buf/src/exec.rs @@ -1,19 +1,25 @@ use std::collections::{HashMap, VecDeque}; use crsn::asm::data::literal::Value; +use crsn::asm::instr::cond::Flag; use crsn::module::{EvalRes, OpTrait}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; use crsn::sexp; -use crsn::sexp::{Sexp, atom_qs}; +use crsn::sexp::{atom_qs, Sexp}; use crsn::utils::A; -use crate::defs::{BufOps, BufValue}; -use crsn::asm::instr::cond::Flag; +use crate::defs::{BufIoMode, BufOps, BufValue}; + +#[derive(Debug, Default, Clone)] +struct Buffer { + data: VecDeque, + iomode: BufIoMode, +} #[derive(Debug, Default)] struct ExtData { - store: HashMap>, + buffers: HashMap, } // TODO optimize and DRY @@ -49,26 +55,38 @@ impl OpTrait for BufOps { }; let data: &mut ExtData = state.ext_mut(); - data.store.insert(id, que); + data.buffers.insert(id, Buffer { + data: que, + iomode: Default::default() + }); state.write(dst, id)?; } + BufOps::SetIoMode { obj, mode } => { + state.clear_status(); + let handle = state.read_obj(obj)?; + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + + buf.iomode = *mode; + } + BufOps::Push { obj, src } => { state.clear_status(); let val = state.read(src)?; let handle = state.read_obj(obj)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - stack.push_back(val); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + buf.data.push_back(val); } BufOps::Pop { dst, obj } => { state.clear_status(); let handle = state.read_obj(obj)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - if let Some(val) = stack.pop_back() { - let empty = stack.is_empty(); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + if let Some(val) = buf.data.pop_back() { + let empty = buf.data.is_empty(); state.write(dst, val)?; state.update_status(val); state.set_flag(Flag::Empty, empty); @@ -82,9 +100,9 @@ impl OpTrait for BufOps { state.clear_status(); let handle = state.read_obj(obj)?; let idx = state.read(idx)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - if let Some(val) = stack.get(idx as usize) { + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + if let Some(val) = buf.data.get(idx as usize) { let val = *val; state.update_status(val); state.write(dst, val)?; @@ -96,9 +114,9 @@ impl OpTrait for BufOps { BufOps::GetLen { dst, obj } => { state.clear_status(); let handle = state.read_obj(obj)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - let val = stack.len() as Value; + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + let val = buf.data.len() as Value; state.write(dst, val)?; state.update_status(val); } @@ -108,12 +126,12 @@ impl OpTrait for BufOps { let handle = state.read_obj(obj)?; let val = state.read(value)?; let idx = state.read(idx)? as usize; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - if idx < stack.len() { - stack[idx] = val; - } else if idx == stack.len() { - stack.push_back(val); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + if idx < buf.data.len() { + buf.data[idx] = val; + } else if idx == buf.data.len() { + buf.data.push_back(val); } else { state.set_flag(Flag::Overflow, true); } @@ -124,12 +142,12 @@ impl OpTrait for BufOps { let handle = state.read_obj(obj)?; let val = state.read(value)?; let idx = state.read(idx)? as usize; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - if idx < stack.len() { - stack.insert(idx, val); - } else if idx == stack.len() { - stack.push_back(val); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + if idx < buf.data.len() { + buf.data.insert(idx, val); + } else if idx == buf.data.len() { + buf.data.push_back(val); } else { state.set_flag(Flag::Overflow, true); } @@ -139,11 +157,11 @@ impl OpTrait for BufOps { state.clear_status(); let handle = state.read_obj(obj)?; let idx = state.read(idx)? as usize; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - if idx < stack.len() { - let val = stack.remove(idx).unwrap(); - let empty = stack.is_empty(); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + if idx < buf.data.len() { + let val = buf.data.remove(idx).unwrap(); + let empty = buf.data.is_empty(); state.update_status(val); state.write(dst, val)?; state.set_flag(Flag::Empty, empty); @@ -155,21 +173,21 @@ impl OpTrait for BufOps { BufOps::Resize { obj, len } => { let handle = state.read_obj(obj)?; let len = state.read(len)? as usize; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - stack.resize(len, 0); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + buf.data.resize(len, 0); } BufOps::Reverse { obj } => { let handle = state.read_obj(obj)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - let len = stack.len(); + let store: &mut ExtData = state.ext_mut(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + let len = buf.data.len(); if len > 1 { let mut start = 0; let mut end = len - 1; while start < end { - stack.swap(start, end); + buf.data.swap(start, end); start += 1; end -= 1; } @@ -179,20 +197,22 @@ impl OpTrait for BufOps { BufOps::AppendBuf { obj, obj2 } => { let handle = state.read_obj(obj)?; let handle2 = state.read_obj(obj2)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack2 = stacks.store.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.clone(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - stack.extend(stack2); + let store: &mut ExtData = state.ext_mut(); + // TODO figure out how to avoid cloning + let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + buf.data.extend(buf2); } BufOps::PrependBuf { obj, obj2 } => { let handle = state.read_obj(obj)?; let handle2 = state.read_obj(obj2)?; - let stacks: &mut ExtData = state.ext_mut(); - let stack2 = stacks.store.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.clone(); - let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; - for v in stack2.into_iter().rev() { - stack.push_front(v); + let store: &mut ExtData = state.ext_mut(); + // TODO figure out how to avoid cloning + let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone(); + let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; + for v in buf2.into_iter().rev() { + buf.data.push_front(v); } } } @@ -257,12 +277,61 @@ impl OpTrait for BufOps { BufOps::PrependBuf { obj, obj2 } => { sexp::list(&[A("bfprep"), A(obj), A(obj2)]) } + BufOps::SetIoMode { obj, mode } => { + sexp::list(&[A("bfio"), A(obj), A(mode.to_value())]) + } } } } pub(crate) fn drop_obj(state: &mut RunState, handle: Value) -> Result, Fault> { - let stacks: &mut ExtData = state.ext_mut(); - Ok(stacks.store.remove(&handle).map(|_| ())) + let store: &mut ExtData = state.ext_mut(); + Ok(store.buffers.remove(&handle).map(|_| ())) +} + +/// Run-time method called to read an object (using the object handle syntax) +pub(crate) fn read_obj(state: &mut RunState, handle: Value) + -> Result, Fault> +{ + let store: &mut ExtData = state.ext_mut(); + if let Some(buf) = store.buffers.get_mut(&handle) { + match match buf.iomode { + BufIoMode::Queue => buf.data.pop_front(), + BufIoMode::ReverseQueue => buf.data.pop_back(), + BufIoMode::Stack => buf.data.pop_back(), + BufIoMode::ReverseStack => buf.data.pop_front() + } { + None => { + state.set_flag(Flag::Overflow, true); + state.set_flag(Flag::Empty, true); + Ok(Some(0)) + } + Some(val) => { + let em = buf.data.is_empty(); + state.set_flag(Flag::Empty, em); + Ok(Some(val)) + } + } + } else { + Ok(None) + } +} + +/// Run-time method called to write an object (using the object handle syntax) +pub(crate) fn write_obj(state: &mut RunState, handle: Value, value: Value) -> Result, Fault> +{ + let store: &mut ExtData = state.ext_mut(); + if let Some(buf) = store.buffers.get_mut(&handle) { + match buf.iomode { + BufIoMode::Queue => buf.data.push_back(value), + BufIoMode::ReverseQueue => buf.data.push_front(value), + BufIoMode::Stack => buf.data.push_back(value), + BufIoMode::ReverseStack => buf.data.push_front(value) + } + + Ok(Some(())) + } else { + Ok(None) + } } diff --git a/crsn_buf/src/lib.rs b/crsn_buf/src/lib.rs index 0f8f175..9f02233 100644 --- a/crsn_buf/src/lib.rs +++ b/crsn_buf/src/lib.rs @@ -6,6 +6,7 @@ use crsn::module::{CrsnExtension, ParseRes}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{RunState, ThreadInfo}; use crsn::sexp::SourcePosition; +use crate::parse::{BUF_IOMODE_STACK, BUF_IOMODE_REVERSE_STACK, BUF_IOMODE_QUEUE, BUF_IOMODE_REVERSE_QUEUE}; mod defs; mod parse; @@ -32,4 +33,30 @@ impl CrsnExtension for BufOps { fn drop_obj(&self, _ti: &ThreadInfo, state: &mut RunState, handle: Value) -> Result, Fault> { exec::drop_obj(state, handle) } + + /// Run-time method called to read an object (using the object handle syntax) + fn read_obj(&self, state: &mut RunState, handle: Value) + -> Result, Fault> + { + exec::read_obj(state, handle) + } + + /// Run-time method called to write an object (using the object handle syntax) + fn write_obj(&self, state: &mut RunState, handle: Value, value: Value) -> Result, Fault> + { + exec::write_obj(state, handle, value) + } + + /// Get value of an extension-provided constant. + /// This constant may be an object handle, or a constant value used as argument in some other instruction. + fn get_constant_value<'a>(&self, name: &str) -> Option + { + match name { + "BFIO_STACK" => Some(BUF_IOMODE_STACK), + "BFIO_RSTACK" => Some(BUF_IOMODE_REVERSE_STACK), + "BFIO_QUEUE" => Some(BUF_IOMODE_QUEUE), + "BFIO_RQUEUE" => Some(BUF_IOMODE_REVERSE_QUEUE), + _ => None + } + } } diff --git a/crsn_buf/src/parse.rs b/crsn_buf/src/parse.rs index 0eb70c2..10e04ac 100644 --- a/crsn_buf/src/parse.rs +++ b/crsn_buf/src/parse.rs @@ -4,9 +4,15 @@ use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; use crsn::sexp::{SourcePosition, Sexp, Atom}; -use crate::defs::{BufOps, BufValue}; +use crate::defs::{BufOps, BufValue, BufIoMode}; use crsn::asm::data::Rd; use crsn::asm::parse::parse_data::parse_rd; +use crsn::asm::data::literal::Value; + +pub const BUF_IOMODE_STACK : Value = 1; +pub const BUF_IOMODE_REVERSE_STACK : Value = 2; +pub const BUF_IOMODE_QUEUE : Value = 3; +pub const BUF_IOMODE_REVERSE_QUEUE : Value = 4; pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { @@ -40,6 +46,21 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar BufOps::New { dst, value } } + "bfio" => { + let obj = args.next_rdobj()?; + let (mode, modepos) = args.next_value()?; + BufOps::SetIoMode { + obj, + mode: match mode { + BUF_IOMODE_STACK => BufIoMode::Stack, + BUF_IOMODE_REVERSE_STACK => BufIoMode::ReverseStack, + BUF_IOMODE_QUEUE => BufIoMode::Queue, + BUF_IOMODE_REVERSE_QUEUE => BufIoMode::ReverseQueue, + _ => return Err(CrsnError::Parse("Bad buffer iomode".into(), modepos)) + } + } + } + "bfrd" => { let dst = args.next_wr()?; let obj = args.next_rdobj()?; diff --git a/crsn_screen/src/defs.rs b/crsn_screen/src/defs.rs index d309804..87c1d7d 100644 --- a/crsn_screen/src/defs.rs +++ b/crsn_screen/src/defs.rs @@ -1,4 +1,5 @@ use crsn::asm::data::{Rd, Wr}; +use crsn::asm::data::literal::Value; #[derive(Clone, Debug, Eq, PartialEq)] pub enum ScreenOp { @@ -31,7 +32,7 @@ pub enum ScreenOp { force: Rd, }, SetOpt { - opt: Rd, + opt: Value, val: Rd, }, } diff --git a/crsn_screen/src/exec.rs b/crsn_screen/src/exec.rs index 7985c33..855d0f0 100644 --- a/crsn_screen/src/exec.rs +++ b/crsn_screen/src/exec.rs @@ -46,8 +46,8 @@ impl Default for Backend { } } -const OPT_AUTO_BLIT: u64 = 1; -const OPT_FRAME_RATE: u64 = 2; +pub const OPT_AUTO_BLIT: u64 = 1; +pub const OPT_FRAME_RATE: u64 = 2; // Hack for Window unsafe impl std::marker::Send for Backend {} @@ -72,11 +72,10 @@ impl OpTrait for ScreenOp { ScreenOp::SetOpt { opt, val } => { state.clear_status(); - let opt = state.read(opt)?; let val = state.read(val)?; let backend: &mut Backend = state.ext_mut(); - match opt { + match *opt { OPT_AUTO_BLIT => { backend.opts.auto_blit = val != 0; debug!("Set auto blit to {:?}", backend.opts.auto_blit); @@ -85,9 +84,8 @@ impl OpTrait for ScreenOp { backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val); debug!("Set frame rate to {:?}", backend.opts.frame_rate); } - other => { - warn!("Bad screen opt: {}", other); - state.set_flag(Flag::Invalid, true); + _other => { + unreachable!() } } } diff --git a/crsn_screen/src/lib.rs b/crsn_screen/src/lib.rs index f4f8c3c..e87b6e7 100644 --- a/crsn_screen/src/lib.rs +++ b/crsn_screen/src/lib.rs @@ -6,6 +6,8 @@ use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes}; use crsn::sexp::SourcePosition; +use crsn::asm::data::literal::Value; +use crate::exec::{OPT_AUTO_BLIT, OPT_FRAME_RATE}; mod defs; mod parse; @@ -28,4 +30,15 @@ impl CrsnExtension for ScreenOps { fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(pos, keyword, args) } + + /// Get value of an extension-provided constant. + /// This constant may be an object handle, or a constant value used as argument in some other instruction. + fn get_constant_value<'a>(&self, name: &str) -> Option + { + match name { + "SCREEN_AUTO_BLIT" => Some(OPT_AUTO_BLIT), + "SCREEN_FPS" => Some(OPT_FRAME_RATE), + _ => None + } + } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index 8ccbb7e..95c0d5b 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -7,6 +7,9 @@ use crsn::sexp::SourcePosition; use crate::defs::ScreenOp; +use super::exec::OPT_AUTO_BLIT; +use super::exec::OPT_FRAME_RATE; + pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { "sc-init" => { @@ -35,8 +38,19 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar } "sc-opt" => { + let (val, valopt) = args.next_value()?; ScreenOp::SetOpt { - opt: args.next_rd()?, + opt: match val { + OPT_AUTO_BLIT => { + OPT_AUTO_BLIT // TODO use enum + } + OPT_FRAME_RATE => { + OPT_FRAME_RATE + } + _ => { + return Err(CrsnError::Parse("Bad screen option".into(), valopt)); + } + }, val: args.next_rd()?, } } diff --git a/examples/buf_io.csn b/examples/buf_io.csn new file mode 100644 index 0000000..56129d9 --- /dev/null +++ b/examples/buf_io.csn @@ -0,0 +1,53 @@ +( + (mkbf r0) + + ; Default mode - queue + (ld @r0 1) + (ld @r0 2) + (ld @r0 3) + (bfrd r1 @r0 2) (cmp r1 3 (ne? (fault))) ; last is 3 + (ld r1 @r0) (cmp r1 1 (ne? (fault))) + (ld r1 @r0) (cmp r1 2 (ne? (fault))) + (ld r1 @r0) (cmp r1 3 (ne? (fault))) + + (bfio @r0 BFIO_QUEUE) + (ld @r0 1) + (ld @r0 2) + (ld @r0 3) + (bfrd r1 @r0 2) (cmp r1 3 (ne? (fault))) ; last is 3 + (ld r1 @r0) (cmp r1 1 (ne? (fault))) + (ld r1 @r0) (cmp r1 2 (ne? (fault))) + (ld r1 @r0) (cmp r1 3 (ne? (fault))) + + (bfio @r0 BFIO_RQUEUE) + (ld @r0 1) + (ld @r0 2) + (ld @r0 3) + (bfrd r1 @r0 0) (cmp r1 3 (ne? (fault))) + (bfrd r1 @r0 2) (cmp r1 1 (ne? (fault))) ; last is 1 + (ld r1 @r0) (cmp r1 1 (ne? (fault))) + (ld r1 @r0) (cmp r1 2 (ne? (fault))) + (ld r1 @r0) (cmp r1 3 (ne? (fault))) + + (bfio @r0 BFIO_STACK) + (ld @r0 1) + (ld @r0 2) + (ld @r0 3) + (bfrd r1 @r0 0) (cmp r1 1 (ne? (fault))) + (bfrd r1 @r0 2) (cmp r1 3 (ne? (fault))) + (ld r1 @r0) (cmp r1 3 (ne? (fault))) + (ld r1 @r0) (cmp r1 2 (ne? (fault))) + (ld r1 @r0) (cmp r1 1 (ne? (fault))) + + (bfio @r0 BFIO_RSTACK) + (ld @r0 1) + (ld @r0 2) + (ld @r0 3) + (bfrd r1 @r0 0) (cmp r1 3 (ne? (fault))) + (bfrd r1 @r0 2) (cmp r1 1 (ne? (fault))) + (ld r1 @r0) (cmp r1 3 (ne? (fault))) + (ld r1 @r0) (cmp r1 2 (ne? (fault))) + (ld r1 @r0) (cmp r1 1 (ne? (fault))) + + (del @r0) +) diff --git a/examples/buf_multi.csn b/examples/buf_multi.csn new file mode 100644 index 0000000..3ce694a --- /dev/null +++ b/examples/buf_multi.csn @@ -0,0 +1,7 @@ +( + (mkbf r0) + (ldn @r0 @cin 5) + (bfrev @r0) + (ldn @cout @r0 5) + (del @r0) +) diff --git a/examples/screen_bounce.csn b/examples/screen_bounce.csn index c367f33..49dbe54 100644 --- a/examples/screen_bounce.csn +++ b/examples/screen_bounce.csn @@ -1,8 +1,8 @@ ; Set log level to "info" or above for the best results! ( (sc-init 800 600) - (sc-opt 1 1) ; auto blit - (sc-opt 2 25) ; frame rate + (sc-opt SCREEN_AUTO_BLIT 1) + (sc-opt SCREEN_FPS 25) (sym x r0) (sym y r1) (sym dx r2) (sym dy r3) (sym color r5)