AMAZING NEW FEATURES ldn, bfio

pull/21/head
Ondřej Hruška 4 years ago
parent 74e3716ce4
commit 4062ff4d09
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 81
      README.md
  2. 9
      crsn/src/asm/parse/arg_parser.rs
  3. 29
      crsn/src/asm/parse/parse_data.rs
  4. 4
      crsn/src/builtin/defs.rs
  5. 11
      crsn/src/builtin/exec.rs
  6. 14
      crsn/src/builtin/parse.rs
  7. 28
      crsn_buf/src/defs.rs
  8. 175
      crsn_buf/src/exec.rs
  9. 27
      crsn_buf/src/lib.rs
  10. 23
      crsn_buf/src/parse.rs
  11. 3
      crsn_screen/src/defs.rs
  12. 12
      crsn_screen/src/exec.rs
  13. 13
      crsn_screen/src/lib.rs
  14. 16
      crsn_screen/src/parse.rs
  15. 53
      examples/buf_io.csn
  16. 7
      examples/buf_multi.csn
  17. 4
      examples/screen_bounce.csn

@ -275,6 +275,8 @@ Jumping to a label is always safer than a manual skip.
## Built-in Instructions ## Built-in Instructions
...and pseudo-instructions
``` ```
; Do nothing ; Do nothing
(nop) (nop)
@ -282,6 +284,15 @@ Jumping to a label is always safer than a manual skip.
; Stop execution ; Stop execution
(halt) (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. ; Mark a jump target.
(:LABEL) (:LABEL)
; Numbered labels ; Numbered labels
@ -300,6 +311,21 @@ Jumping to a label is always safer than a manual skip.
; Skip backward or forward ; Skip backward or forward
(s Rd) (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). ; Mark a routine entry point (call target).
(routine PROC) (routine PROC)
(routine PROC/A) (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 ; Exit the current routine with return values
(ret Rd...) (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. ; Deny jumps, skips and run across this address, producing a run-time fault with a message.
(barrier) (barrier)
(barrier message) (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. ; 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-open LABEL)
(barrier-close 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 ## Arithmetic Module
@ -460,6 +465,24 @@ A buffer needs to be created using one of the init instructions:
(mkbf Wr (Rd...)) (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) Primitive buffer ops (position is always 0-based)
```lisp ```lisp
@ -540,9 +563,9 @@ such as animations.
; Set pixel color ; Set pixel color
(sc-px X Y COLOR) (sc-px X Y COLOR)
; Set screen option ; Set screen option. Constants are pre-defined.
; 1 ... auto-blit (blit automatically on pixel write when needed to achieve the target FPS) ; SCREEN_AUTO_BLIT (1) ... auto-blit (blit automatically on pixel write when needed to achieve the target FPS)
; 2 ... frame rate ; SCREEN_FPS (2) ... frame rate
(sc-opt OPTION VALUE) (sc-opt OPTION VALUE)
; Blit (render the pixel buffer). ; Blit (render the pixel buffer).

@ -2,9 +2,10 @@ use sexp::{Sexp, SourcePosition};
use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr}; use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr};
use crate::asm::error::CrsnError; 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::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::asm::data::literal::Value;
/// Utility for argument parsing /// Utility for argument parsing
#[derive(Debug)] #[derive(Debug)]
@ -85,6 +86,12 @@ impl<'a> TokenParser<'a> {
Ok(esa) 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 /// Get the next entry as read location
pub fn next_rd(&mut self) -> Result<Rd, CrsnError> { pub fn next_rd(&mut self) -> Result<Rd, CrsnError> {
let next = self.next_or_err()?; let next = self.next_or_err()?;

@ -12,7 +12,7 @@ use crate::asm::patches::ErrWithPos;
fn is_valid_identifier(name: &str) -> bool { fn is_valid_identifier(name: &str) -> bool {
// ascii symbols "!\"#$_&'()*+,-./:;<=>?@[\\]^_`{|}~" // ascii symbols "!\"#$_&'()*+,-./:;<=>?@[\\]^_`{|}~"
const BLACKLIST : &str = "!\"#$&'()*+,-./:;<=>?@[\\]^`{|}~"; const BLACKLIST : &str = "!\"#$&'()*+,./;<=>?@[\\]^`{|}~";
name != "_" name != "_"
&& !name.starts_with(|c: char| c.is_ascii_digit() || BLACKLIST.contains(c)) && !name.starts_with(|c: char| c.is_ascii_digit() || BLACKLIST.contains(c))
&& !name.contains(|c : char| c.is_whitespace() || 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<DataDisp, CrsnE
} }
/// Parse immediate value /// Parse immediate value
pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<Value, CrsnError> { pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<(Value, SourcePosition), CrsnError> {
match tok { match tok {
Sexp::Atom(Atom::I(val), _pos) => { Sexp::Atom(Atom::I(val), pos) => {
Ok(unsafe { std::mem::transmute(val) }) Ok((unsafe { std::mem::transmute(val) }, pos))
} }
Sexp::Atom(Atom::U(val), _pos) => { Sexp::Atom(Atom::U(val), pos) => {
Ok(val) Ok((val, pos))
} }
Sexp::Atom(Atom::C(val), _pos) => { Sexp::Atom(Atom::C(val), pos) => {
Ok(val as u64) Ok((val as u64, pos))
} }
Sexp::Atom(Atom::QS(_), pos) => { Sexp::Atom(Atom::QS(_), pos) => {
Err(CrsnError::Parse("quoted string not expected here".into(), pos)) Err(CrsnError::Parse("quoted string not expected here".into(), pos))
} }
Sexp::Atom(Atom::F(val), _) => { Sexp::Atom(Atom::F(val), pos) => {
Ok(unsafe { std::mem::transmute(val) }) Ok((unsafe { std::mem::transmute(val) }, pos))
} }
Sexp::Atom(Atom::S(s), pos) => { Sexp::Atom(Atom::S(s), pos) => {
let pstate = pcx.state.borrow(); let pstate = pcx.state.borrow();
if let Some(val) = pstate.constants.get(&s) { 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)) Err(CrsnError::Parse(format!("unknown constant: {}", s).into(), pos))

@ -70,8 +70,10 @@ pub enum BuiltinOp {
/// Deallocate an extension object. /// Deallocate an extension object.
/// The object is released and the handle becomes invalid. /// The object is released and the handle becomes invalid.
Delete(RdObj), Delete(RdObj),
/// Copy value /// Move a value
MoveValue { dst: Wr, src: Rd }, MoveValue { dst: Wr, src: Rd },
/// Move N values
MoveMultipleValues { dst: Wr, src: Rd, count: Rd },
/// Swap two registers /// Swap two registers
SwapValues { a: RdWr, b: RdWr }, SwapValues { a: RdWr, b: RdWr },
/// Store runtime status to a register /// Store runtime status to a register

@ -109,6 +109,17 @@ impl OpTrait for BuiltinOp {
state.update_status(val); state.update_status(val);
state.write(dst, 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 } => { BuiltinOp::SwapValues { a, b } => {
let aa = state.read(a)?; let aa = state.read(a)?;
let bb = state.read(b)?; let bb = state.read(b)?;

@ -88,7 +88,7 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
"def" => { "def" => {
let (name, namepos) = parse_constant_name(args.next_or_err()?)?; 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(); let mut pstate = pcx.state.borrow_mut();
if pstate.constants.contains_key(&name) { 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" => { "swap" => {
BuiltinOp::SwapValues { BuiltinOp::SwapValues {
a: args.next_rdwr()?, 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::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
BuiltinOp::MoveValue { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]), 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::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]) BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)])
} }
@ -382,6 +391,7 @@ mod test {
("(ld r0 r0)", "(ld r0 r0)"), ("(ld r0 r0)", "(ld r0 r0)"),
("(ld r0 156)", "(ld r0 156)"), ("(ld r0 156)", "(ld r0 156)"),
("(ld _ -32767)", "(ld _ -32767)"), ("(ld _ -32767)", "(ld _ -32767)"),
("(ldn _ @r0 7)", "(ldn _ @r0 7)"),
("(stf r0)", "(stf r0)"), ("(stf r0)", "(stf r0)"),
("(ldf r0)", "(ldf r0)"), ("(ldf r0)", "(ldf r0)"),
("(far :label)", "(far :label)"), ("(far :label)", "(far :label)"),

@ -1,4 +1,6 @@
use crsn::asm::data::{Rd, RdObj, Wr}; 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)] #[derive(Clone, Debug, PartialEq)]
pub enum BufValue { pub enum BufValue {
@ -7,9 +9,35 @@ pub enum BufValue {
Chars(String), 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)] #[derive(Clone, Debug, PartialEq)]
pub enum BufOps { pub enum BufOps {
New { dst: Wr, value: BufValue }, New { dst: Wr, value: BufValue },
SetIoMode { obj: RdObj, mode: BufIoMode },
// Elementary ops // Elementary ops
Read { dst: Wr, obj: RdObj, idx: Rd }, Read { dst: Wr, obj: RdObj, idx: Rd },

@ -1,19 +1,25 @@
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use crsn::asm::data::literal::Value; use crsn::asm::data::literal::Value;
use crsn::asm::instr::cond::Flag;
use crsn::module::{EvalRes, OpTrait}; use crsn::module::{EvalRes, OpTrait};
use crsn::runtime::fault::Fault; use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; use crsn::runtime::run_thread::{state::RunState, ThreadInfo};
use crsn::sexp; use crsn::sexp;
use crsn::sexp::{Sexp, atom_qs}; use crsn::sexp::{atom_qs, Sexp};
use crsn::utils::A; use crsn::utils::A;
use crate::defs::{BufOps, BufValue}; use crate::defs::{BufIoMode, BufOps, BufValue};
use crsn::asm::instr::cond::Flag;
#[derive(Debug, Default, Clone)]
struct Buffer {
data: VecDeque<Value>,
iomode: BufIoMode,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct ExtData { struct ExtData {
store: HashMap<Value, VecDeque<Value>>, buffers: HashMap<Value, Buffer>,
} }
// TODO optimize and DRY // TODO optimize and DRY
@ -49,26 +55,38 @@ impl OpTrait for BufOps {
}; };
let data: &mut ExtData = state.ext_mut(); 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)?; 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 } => { BufOps::Push { obj, src } => {
state.clear_status(); state.clear_status();
let val = state.read(src)?; let val = state.read(src)?;
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
stack.push_back(val); buf.data.push_back(val);
} }
BufOps::Pop { dst, obj } => { BufOps::Pop { dst, obj } => {
state.clear_status(); state.clear_status();
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
if let Some(val) = stack.pop_back() { if let Some(val) = buf.data.pop_back() {
let empty = stack.is_empty(); let empty = buf.data.is_empty();
state.write(dst, val)?; state.write(dst, val)?;
state.update_status(val); state.update_status(val);
state.set_flag(Flag::Empty, empty); state.set_flag(Flag::Empty, empty);
@ -82,9 +100,9 @@ impl OpTrait for BufOps {
state.clear_status(); state.clear_status();
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let idx = state.read(idx)?; let idx = state.read(idx)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
if let Some(val) = stack.get(idx as usize) { if let Some(val) = buf.data.get(idx as usize) {
let val = *val; let val = *val;
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
@ -96,9 +114,9 @@ impl OpTrait for BufOps {
BufOps::GetLen { dst, obj } => { BufOps::GetLen { dst, obj } => {
state.clear_status(); state.clear_status();
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
let val = stack.len() as Value; let val = buf.data.len() as Value;
state.write(dst, val)?; state.write(dst, val)?;
state.update_status(val); state.update_status(val);
} }
@ -108,12 +126,12 @@ impl OpTrait for BufOps {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let val = state.read(value)?; let val = state.read(value)?;
let idx = state.read(idx)? as usize; let idx = state.read(idx)? as usize;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
if idx < stack.len() { if idx < buf.data.len() {
stack[idx] = val; buf.data[idx] = val;
} else if idx == stack.len() { } else if idx == buf.data.len() {
stack.push_back(val); buf.data.push_back(val);
} else { } else {
state.set_flag(Flag::Overflow, true); state.set_flag(Flag::Overflow, true);
} }
@ -124,12 +142,12 @@ impl OpTrait for BufOps {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let val = state.read(value)?; let val = state.read(value)?;
let idx = state.read(idx)? as usize; let idx = state.read(idx)? as usize;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
if idx < stack.len() { if idx < buf.data.len() {
stack.insert(idx, val); buf.data.insert(idx, val);
} else if idx == stack.len() { } else if idx == buf.data.len() {
stack.push_back(val); buf.data.push_back(val);
} else { } else {
state.set_flag(Flag::Overflow, true); state.set_flag(Flag::Overflow, true);
} }
@ -139,11 +157,11 @@ impl OpTrait for BufOps {
state.clear_status(); state.clear_status();
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let idx = state.read(idx)? as usize; let idx = state.read(idx)? as usize;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
if idx < stack.len() { if idx < buf.data.len() {
let val = stack.remove(idx).unwrap(); let val = buf.data.remove(idx).unwrap();
let empty = stack.is_empty(); let empty = buf.data.is_empty();
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
state.set_flag(Flag::Empty, empty); state.set_flag(Flag::Empty, empty);
@ -155,21 +173,21 @@ impl OpTrait for BufOps {
BufOps::Resize { obj, len } => { BufOps::Resize { obj, len } => {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let len = state.read(len)? as usize; let len = state.read(len)? as usize;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
stack.resize(len, 0); buf.data.resize(len, 0);
} }
BufOps::Reverse { obj } => { BufOps::Reverse { obj } => {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
let len = stack.len(); let len = buf.data.len();
if len > 1 { if len > 1 {
let mut start = 0; let mut start = 0;
let mut end = len - 1; let mut end = len - 1;
while start < end { while start < end {
stack.swap(start, end); buf.data.swap(start, end);
start += 1; start += 1;
end -= 1; end -= 1;
} }
@ -179,20 +197,22 @@ impl OpTrait for BufOps {
BufOps::AppendBuf { obj, obj2 } => { BufOps::AppendBuf { obj, obj2 } => {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let handle2 = state.read_obj(obj2)?; let handle2 = state.read_obj(obj2)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack2 = stacks.store.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.clone(); // TODO figure out how to avoid cloning
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone();
stack.extend(stack2); let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
buf.data.extend(buf2);
} }
BufOps::PrependBuf { obj, obj2 } => { BufOps::PrependBuf { obj, obj2 } => {
let handle = state.read_obj(obj)?; let handle = state.read_obj(obj)?;
let handle2 = state.read_obj(obj2)?; let handle2 = state.read_obj(obj2)?;
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
let stack2 = stacks.store.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.clone(); // TODO figure out how to avoid cloning
let stack = stacks.store.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?; let buf2 = store.buffers.get(&handle2).ok_or_else(|| Fault::ObjectNotExist(handle))?.data.clone();
for v in stack2.into_iter().rev() { let buf = store.buffers.get_mut(&handle).ok_or_else(|| Fault::ObjectNotExist(handle))?;
stack.push_front(v); for v in buf2.into_iter().rev() {
buf.data.push_front(v);
} }
} }
} }
@ -257,12 +277,61 @@ impl OpTrait for BufOps {
BufOps::PrependBuf { obj, obj2 } => { BufOps::PrependBuf { obj, obj2 } => {
sexp::list(&[A("bfprep"), A(obj), A(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<Option<()>, Fault> { pub(crate) fn drop_obj(state: &mut RunState, handle: Value) -> Result<Option<()>, Fault> {
let stacks: &mut ExtData = state.ext_mut(); let store: &mut ExtData = state.ext_mut();
Ok(stacks.store.remove(&handle).map(|_| ())) 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<Option<Value>, 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<Option<()>, 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)
}
} }

@ -6,6 +6,7 @@ use crsn::module::{CrsnExtension, ParseRes};
use crsn::runtime::fault::Fault; use crsn::runtime::fault::Fault;
use crsn::runtime::run_thread::{RunState, ThreadInfo}; use crsn::runtime::run_thread::{RunState, ThreadInfo};
use crsn::sexp::SourcePosition; use crsn::sexp::SourcePosition;
use crate::parse::{BUF_IOMODE_STACK, BUF_IOMODE_REVERSE_STACK, BUF_IOMODE_QUEUE, BUF_IOMODE_REVERSE_QUEUE};
mod defs; mod defs;
mod parse; mod parse;
@ -32,4 +33,30 @@ impl CrsnExtension for BufOps {
fn drop_obj(&self, _ti: &ThreadInfo, state: &mut RunState, handle: Value) -> Result<Option<()>, Fault> { fn drop_obj(&self, _ti: &ThreadInfo, state: &mut RunState, handle: Value) -> Result<Option<()>, Fault> {
exec::drop_obj(state, handle) 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<Option<Value>, 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<Option<()>, 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<Value>
{
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
}
}
} }

@ -4,9 +4,15 @@ use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::ParseRes; use crsn::module::ParseRes;
use crsn::sexp::{SourcePosition, Sexp, Atom}; 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::data::Rd;
use crsn::asm::parse::parse_data::parse_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<ParseRes<'a, OpKind>, CrsnError> { pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
Ok(ParseRes::ext(match keyword { 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 } 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" => { "bfrd" => {
let dst = args.next_wr()?; let dst = args.next_wr()?;
let obj = args.next_rdobj()?; let obj = args.next_rdobj()?;

@ -1,4 +1,5 @@
use crsn::asm::data::{Rd, Wr}; use crsn::asm::data::{Rd, Wr};
use crsn::asm::data::literal::Value;
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum ScreenOp { pub enum ScreenOp {
@ -31,7 +32,7 @@ pub enum ScreenOp {
force: Rd, force: Rd,
}, },
SetOpt { SetOpt {
opt: Rd, opt: Value,
val: Rd, val: Rd,
}, },
} }

@ -46,8 +46,8 @@ impl Default for Backend {
} }
} }
const OPT_AUTO_BLIT: u64 = 1; pub const OPT_AUTO_BLIT: u64 = 1;
const OPT_FRAME_RATE: u64 = 2; pub const OPT_FRAME_RATE: u64 = 2;
// Hack for Window // Hack for Window
unsafe impl std::marker::Send for Backend {} unsafe impl std::marker::Send for Backend {}
@ -72,11 +72,10 @@ impl OpTrait for ScreenOp {
ScreenOp::SetOpt { opt, val } => { ScreenOp::SetOpt { opt, val } => {
state.clear_status(); state.clear_status();
let opt = state.read(opt)?;
let val = state.read(val)?; let val = state.read(val)?;
let backend: &mut Backend = state.ext_mut(); let backend: &mut Backend = state.ext_mut();
match opt { match *opt {
OPT_AUTO_BLIT => { OPT_AUTO_BLIT => {
backend.opts.auto_blit = val != 0; backend.opts.auto_blit = val != 0;
debug!("Set auto blit to {:?}", backend.opts.auto_blit); 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); backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val);
debug!("Set frame rate to {:?}", backend.opts.frame_rate); debug!("Set frame rate to {:?}", backend.opts.frame_rate);
} }
other => { _other => {
warn!("Bad screen opt: {}", other); unreachable!()
state.set_flag(Flag::Invalid, true);
} }
} }
} }

@ -6,6 +6,8 @@ use crsn::asm::instr::op::OpKind;
use crsn::asm::parse::arg_parser::TokenParser; use crsn::asm::parse::arg_parser::TokenParser;
use crsn::module::{CrsnExtension, ParseRes}; use crsn::module::{CrsnExtension, ParseRes};
use crsn::sexp::SourcePosition; use crsn::sexp::SourcePosition;
use crsn::asm::data::literal::Value;
use crate::exec::{OPT_AUTO_BLIT, OPT_FRAME_RATE};
mod defs; mod defs;
mod parse; mod parse;
@ -28,4 +30,15 @@ impl CrsnExtension for ScreenOps {
fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
parse::parse(pos, keyword, args) 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<Value>
{
match name {
"SCREEN_AUTO_BLIT" => Some(OPT_AUTO_BLIT),
"SCREEN_FPS" => Some(OPT_FRAME_RATE),
_ => None
}
}
} }

@ -7,6 +7,9 @@ use crsn::sexp::SourcePosition;
use crate::defs::ScreenOp; 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<ParseRes<'a, OpKind>, CrsnError> { pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
Ok(ParseRes::ext(match keyword { Ok(ParseRes::ext(match keyword {
"sc-init" => { "sc-init" => {
@ -35,8 +38,19 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar
} }
"sc-opt" => { "sc-opt" => {
let (val, valopt) = args.next_value()?;
ScreenOp::SetOpt { 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()?, val: args.next_rd()?,
} }
} }

@ -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)
)

@ -0,0 +1,7 @@
(
(mkbf r0)
(ldn @r0 @cin 5)
(bfrev @r0)
(ldn @cout @r0 5)
(del @r0)
)

@ -1,8 +1,8 @@
; Set log level to "info" or above for the best results! ; Set log level to "info" or above for the best results!
( (
(sc-init 800 600) (sc-init 800 600)
(sc-opt 1 1) ; auto blit (sc-opt SCREEN_AUTO_BLIT 1)
(sc-opt 2 25) ; frame rate (sc-opt SCREEN_FPS 25)
(sym x r0) (sym y r1) (sym dx r2) (sym dy r3) (sym color r5) (sym x r0) (sym y r1) (sym dx r2) (sym dy r3) (sym color r5)

Loading…
Cancel
Save