forked from MightyPork/crsn
Example extension: Stacks; fixes to allow module data storage in thread context
This commit is contained in:
Generated
+8
@@ -71,6 +71,13 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crsn_stacks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crsn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clonable"
|
||||
version = "0.9.0"
|
||||
@@ -114,6 +121,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"crsn",
|
||||
"crsn_arith",
|
||||
"crsn_stacks",
|
||||
"log",
|
||||
"simple_logger",
|
||||
"thiserror",
|
||||
|
||||
@@ -3,4 +3,5 @@ members = [
|
||||
"launcher",
|
||||
"crsn",
|
||||
"crsn_arith",
|
||||
"crsn_stacks",
|
||||
]
|
||||
|
||||
+10
-14
@@ -1,19 +1,15 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::asm::data::{
|
||||
literal::DebugMsg, literal::Label,
|
||||
literal::RoutineName,
|
||||
Rd,
|
||||
Wr,
|
||||
};
|
||||
use crate::asm::data::literal::Addr;
|
||||
|
||||
|
||||
use crate::asm::error::Error;
|
||||
use crate::asm::instr::Cond;
|
||||
|
||||
use crate::asm::parse::arg_parser::ArgParser;
|
||||
use crate::builtin::defs::BuiltinOp;
|
||||
use crate::runtime::fault::Fault;
|
||||
use crate::runtime::frame::{CallStack, StackFrame};
|
||||
use crate::runtime::program::Program;
|
||||
|
||||
|
||||
use crate::runtime::run_thread::{RunState, ThreadInfo};
|
||||
|
||||
/// A higher level simple opration
|
||||
#[derive(Debug)]
|
||||
@@ -24,7 +20,7 @@ pub enum Op {
|
||||
}
|
||||
|
||||
pub trait OpTrait: Debug + Send + Sync + 'static {
|
||||
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault>;
|
||||
fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault>;
|
||||
}
|
||||
|
||||
pub enum ParseOpResult {
|
||||
@@ -64,13 +60,13 @@ impl Default for EvalRes {
|
||||
|
||||
|
||||
impl OpTrait for Op {
|
||||
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> {
|
||||
fn execute(&self, ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
||||
match self {
|
||||
Op::BuiltIn(op) => {
|
||||
op.execute(&program, call_stack, frame)
|
||||
op.execute(ti, state)
|
||||
}
|
||||
Op::Ext(op) => {
|
||||
op.execute(&program, call_stack, frame)
|
||||
op.execute(ti, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::asm::instr::Op;
|
||||
|
||||
use crate::asm::instr::op::AsmModule;
|
||||
use crate::runtime::program::Program;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::asm::data::literal::{Label, RoutineName};
|
||||
|
||||
use crate::asm::error::Error;
|
||||
use crate::asm::instr::Op;
|
||||
use crate::asm::instr::cond::parse_cond;
|
||||
|
||||
use crate::asm::instr::op::{AsmModule, ParseOpResult};
|
||||
use crate::asm::parse::arg_parser::ArgParser;
|
||||
use crate::asm::parse::parse_data::{parse_label, parse_rd};
|
||||
use crate::asm::parse::sexp_expect::expect_string_atom;
|
||||
|
||||
|
||||
use crate::builtin::parse::BuiltinOpParser;
|
||||
|
||||
pub fn parse_op(keyword: &str, mut arg_tokens: ArgParser, parsers: &[Box<dyn AsmModule>]) -> Result<Op, Error> {
|
||||
|
||||
@@ -6,6 +6,8 @@ use crate::asm::data::{Rd, Wr};
|
||||
pub enum BuiltinOp {
|
||||
/// Do nothing
|
||||
Nop,
|
||||
/// Stop execution
|
||||
Halt,
|
||||
/// Mark a jump target.
|
||||
Label(Label),
|
||||
/// Jump to a label
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
use std::ops::Rem;
|
||||
|
||||
use num_traits::PrimInt;
|
||||
|
||||
|
||||
|
||||
use crate::asm::instr::op::{OpTrait, EvalRes};
|
||||
use crate::runtime::fault::Fault;
|
||||
use crate::runtime::frame::{CallStack, StackFrame};
|
||||
use crate::runtime::program::Program;
|
||||
use crate::runtime::frame::{StackFrame};
|
||||
|
||||
use crate::builtin::defs::BuiltinOp;
|
||||
use crate::asm::data::literal::Addr;
|
||||
use crate::runtime::run_thread::{ThreadInfo, RunState};
|
||||
|
||||
impl OpTrait for BuiltinOp {
|
||||
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> {
|
||||
fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
||||
let frame = &mut state.frame;
|
||||
let program = &info.program;
|
||||
let mut res = EvalRes::default();
|
||||
match self {
|
||||
BuiltinOp::Nop => {}
|
||||
BuiltinOp::Halt => {
|
||||
trace!("HALT - stop execution");
|
||||
return Err(Fault::Halt);
|
||||
}
|
||||
BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => {
|
||||
/* this is nop, but without any cost - just markers */
|
||||
res.cycles = 0;
|
||||
@@ -82,7 +89,7 @@ impl OpTrait for BuiltinOp {
|
||||
|
||||
let mut frame2 = StackFrame::new(pos, &values);
|
||||
std::mem::swap(frame, &mut frame2);
|
||||
call_stack.push(frame2);
|
||||
state.call_stack.push(frame2);
|
||||
res.advance = 0;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -91,7 +98,7 @@ impl OpTrait for BuiltinOp {
|
||||
}
|
||||
}
|
||||
BuiltinOp::Ret(retvals) => {
|
||||
match call_stack.pop() {
|
||||
match state.call_stack.pop() {
|
||||
Some(previous) => {
|
||||
let mut values = Vec::with_capacity(retvals.len());
|
||||
for arg in retvals {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::asm::data::{Rd, Wr};
|
||||
|
||||
use crate::asm::error::{Error};
|
||||
use crate::asm::instr::op::{AsmModule, ParseOpResult};
|
||||
use crate::asm::parse::arg_parser::ArgParser;
|
||||
@@ -30,6 +30,14 @@ impl AsmModule for BuiltinOpParser {
|
||||
|
||||
fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> {
|
||||
Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword {
|
||||
"nop" => {
|
||||
BuiltinOp::Nop
|
||||
}
|
||||
|
||||
"halt" => {
|
||||
BuiltinOp::Halt
|
||||
}
|
||||
|
||||
"j" => {
|
||||
let dest = parse_label(args.next())?;
|
||||
BuiltinOp::Jump(dest)
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
use crate::asm::data::literal::Addr;
|
||||
use crate::asm::instr::Op;
|
||||
|
||||
|
||||
use crate::asm::instr::op::{EvalRes, OpTrait};
|
||||
use crate::runtime::fault::Fault;
|
||||
use crate::runtime::frame::StackFrame;
|
||||
|
||||
use crate::runtime::run_thread::RunThread;
|
||||
|
||||
impl RunThread {
|
||||
// TODO unit tests
|
||||
pub fn eval_op(&mut self) -> Result<EvalRes, Fault> {
|
||||
let frame = &mut self.frame;
|
||||
|
||||
let op = self.program.read(frame.pc);
|
||||
let state = &mut self.state;
|
||||
let info = &self.info;
|
||||
|
||||
trace!("### {:04} : {:?}", frame.pc.0, op);
|
||||
let op = info.program.read(state.frame.pc);
|
||||
|
||||
trace!("### {:04} : {:?}", state.frame.pc.0, op);
|
||||
|
||||
/* Operations can be given different execution times when run in slow mode. */
|
||||
/* Presently, all that do anything use 1 cycle. */
|
||||
|
||||
let rv = op.execute(&self.program, &mut self.call_stack, frame);
|
||||
let rv = op.execute(info, state);
|
||||
trace!("-> {:?}", rv);
|
||||
rv
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ pub enum Fault {
|
||||
msg: DebugMsg,
|
||||
},
|
||||
|
||||
#[error("Program ended.")]
|
||||
Halt,
|
||||
|
||||
// #[error("Memory region {area:?} is locked by thread {owner:?}")]
|
||||
// MemoryLocked {
|
||||
// area: MemorySpan,
|
||||
|
||||
@@ -8,36 +8,58 @@ use crate::asm::data::literal::Addr;
|
||||
use crate::asm::instr::op::EvalRes;
|
||||
use crate::runtime::frame::{CallStack, StackFrame};
|
||||
use crate::runtime::program::Program;
|
||||
|
||||
const CYCLE_TIME: Duration = Duration::from_millis(0);
|
||||
//const CYCLE_TIME : Duration = Duration::from_millis(100);
|
||||
use crate::runtime::fault::Fault;
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
pub struct ThreadToken(pub u32);
|
||||
|
||||
pub struct RunThread {
|
||||
pub(crate) info: ThreadInfo,
|
||||
pub(crate) state: RunState,
|
||||
}
|
||||
|
||||
pub struct ThreadInfo {
|
||||
/// Thread ID
|
||||
pub id: ThreadToken,
|
||||
/// Program to run
|
||||
pub program: Arc<Program>,
|
||||
/// Program to run
|
||||
pub cycle_time: Duration,
|
||||
}
|
||||
|
||||
pub struct RunState {
|
||||
/// Active stack frame
|
||||
pub frame: StackFrame,
|
||||
/// Call stack
|
||||
pub call_stack: CallStack,
|
||||
/// Program to run
|
||||
pub program: Arc<Program>,
|
||||
/// Extension data
|
||||
ext_data: HashMap<TypeId, Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
impl RunState {
|
||||
pub fn ext_mut<T: Send + Default + 'static>(&mut self) -> &mut T {
|
||||
if !self.ext_data.contains_key(&TypeId::of::<T>()) {
|
||||
self.ext_data.insert(TypeId::of::<T>(), Box::new(T::default()));
|
||||
}
|
||||
|
||||
self.ext_data.get_mut(&TypeId::of::<T>()).unwrap()
|
||||
.downcast_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl RunThread {
|
||||
pub fn new(id: ThreadToken, program: Arc<Program>, pc: Addr, args: &[u64]) -> Self {
|
||||
let sf = StackFrame::new(pc, args);
|
||||
|
||||
Self {
|
||||
info: ThreadInfo {
|
||||
id,
|
||||
frame: sf,
|
||||
call_stack: vec![],
|
||||
program,
|
||||
cycle_time: Duration::default(),
|
||||
},
|
||||
state: RunState {
|
||||
frame: StackFrame::new(pc, args),
|
||||
call_stack: vec![],
|
||||
ext_data: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,12 +72,17 @@ impl RunThread {
|
||||
fn run(mut self) {
|
||||
'run: loop {
|
||||
match self.eval_op() {
|
||||
Ok(EvalRes {
|
||||
cycles, advance
|
||||
}) => {
|
||||
std::thread::sleep(CYCLE_TIME * (cycles as u32));
|
||||
trace!("Step {}; Status = {}", advance, self.frame.status);
|
||||
self.frame.pc.advance(advance);
|
||||
Ok(EvalRes { cycles, advance }) => {
|
||||
std::thread::sleep(self.info.cycle_time * (cycles as u32));
|
||||
trace!("Step {}; Status = {}", advance, self.state.frame.status);
|
||||
self.state.frame.pc.advance(advance);
|
||||
if self.state.frame.status.invalid {
|
||||
warn!("Operation failed with INVALID status!");
|
||||
}
|
||||
}
|
||||
Err(Fault::Halt) => {
|
||||
info!("Program ended.");
|
||||
break 'run;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Fault: {:?}", e);
|
||||
@@ -64,13 +91,4 @@ impl RunThread {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ext_mut<T: Send + Default + 'static>(&mut self) -> &mut T {
|
||||
if !self.ext_data.contains_key(&TypeId::of::<T>()) {
|
||||
self.ext_data.insert(TypeId::of::<T>(), Box::new(T::default()));
|
||||
}
|
||||
|
||||
self.ext_data.get_mut(&TypeId::of::<T>()).unwrap()
|
||||
.downcast_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ use num_traits::PrimInt;
|
||||
|
||||
use crsn::asm::instr::op::{OpTrait, EvalRes};
|
||||
use crsn::runtime::fault::Fault;
|
||||
use crsn::runtime::frame::{CallStack, StackFrame};
|
||||
use crsn::runtime::program::Program;
|
||||
|
||||
|
||||
|
||||
use crate::defs::ArithOp;
|
||||
use crsn::runtime::run_thread::{ThreadInfo, RunState};
|
||||
|
||||
impl OpTrait for ArithOp {
|
||||
fn execute(&self, _program: &Program, _call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> {
|
||||
fn execute(&self, _ti : &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
||||
let frame = &mut state.frame;
|
||||
let eres = EvalRes::default();
|
||||
match self {
|
||||
ArithOp::Test { a } => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub use parse::ArithOpParser;
|
||||
pub use parse::ArithOps;
|
||||
|
||||
mod defs;
|
||||
mod parse;
|
||||
|
||||
@@ -7,11 +7,11 @@ use crsn::asm::parse::arg_parser::ArgParser;
|
||||
use crate::defs::ArithOp;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ArithOpParser {
|
||||
pub struct ArithOps {
|
||||
_internal: ()
|
||||
}
|
||||
|
||||
impl ArithOpParser {
|
||||
impl ArithOps {
|
||||
pub fn new() -> Box<dyn AsmModule> {
|
||||
Box::new(Self {
|
||||
_internal: ()
|
||||
@@ -19,7 +19,7 @@ impl ArithOpParser {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsmModule for ArithOpParser {
|
||||
impl AsmModule for ArithOps {
|
||||
fn name(&self) -> &'static str {
|
||||
"arith"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "crsn_stacks"
|
||||
version = "0.1.0"
|
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
crsn = { path = "../crsn" }
|
||||
@@ -0,0 +1,7 @@
|
||||
use crsn::asm::data::{Rd, Wr};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum StackOp {
|
||||
Push { num: Rd, src: Rd },
|
||||
Pop { dst: Wr, num: Rd },
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
|
||||
use crsn::asm::data::literal::Value;
|
||||
use crsn::asm::instr::op::{EvalRes, OpTrait};
|
||||
use crsn::runtime::fault::Fault;
|
||||
|
||||
|
||||
use crsn::runtime::run_thread::{RunState, ThreadInfo};
|
||||
|
||||
use crate::defs::StackOp;
|
||||
|
||||
struct Stacks {
|
||||
stacks: Vec<VecDeque<Value>>,
|
||||
}
|
||||
|
||||
impl Default for Stacks {
|
||||
fn default() -> Self {
|
||||
Stacks {
|
||||
stacks: vec![VecDeque::default(); 8],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpTrait for StackOp {
|
||||
fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
||||
let eres = EvalRes::default();
|
||||
match self {
|
||||
StackOp::Push { num, src } => {
|
||||
state.frame.status.clear();
|
||||
let stack_num = state.frame.read(*num)?;
|
||||
let val = state.frame.read(*src)?;
|
||||
|
||||
if stack_num > 8 {
|
||||
state.frame.status.invalid = true;
|
||||
} else {
|
||||
let obj: &mut Stacks = state.ext_mut();
|
||||
obj.stacks[stack_num as usize].push_back(val);
|
||||
}
|
||||
}
|
||||
StackOp::Pop { dst, num } => {
|
||||
state.frame.status.clear();
|
||||
let stack_num = state.frame.read(*num)?;
|
||||
|
||||
if stack_num > 8 {
|
||||
state.frame.status.invalid = true;
|
||||
} else {
|
||||
let obj: &mut Stacks = state.ext_mut();
|
||||
let val = obj.stacks[stack_num as usize].pop_back();
|
||||
|
||||
let val = match val {
|
||||
None => {
|
||||
state.frame.status.overflow = true;
|
||||
0
|
||||
}
|
||||
Some(val) => {
|
||||
val
|
||||
}
|
||||
};
|
||||
|
||||
state.frame.write(*dst, val)?;
|
||||
}
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
Ok(eres)
|
||||
}
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub use parse::StackOps;
|
||||
|
||||
mod defs;
|
||||
mod parse;
|
||||
mod exec;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
use crsn::asm::error::Error;
|
||||
use crsn::asm::instr::op::{AsmModule, ParseOpResult};
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::parse::arg_parser::ArgParser;
|
||||
|
||||
use crate::defs::StackOp;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StackOps {
|
||||
_internal: ()
|
||||
}
|
||||
|
||||
impl StackOps {
|
||||
pub fn new() -> Box<dyn AsmModule> {
|
||||
Box::new(Self {
|
||||
_internal: ()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsmModule for StackOps {
|
||||
fn name(&self) -> &'static str {
|
||||
"stacks"
|
||||
}
|
||||
|
||||
fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> {
|
||||
Ok(ParseOpResult::Parsed(Op::Ext(Box::new(match keyword {
|
||||
"push" => {
|
||||
StackOp::Push {
|
||||
num: args.next_rd()?,
|
||||
src: args.next_rd()?,
|
||||
}
|
||||
}
|
||||
|
||||
"pop" => {
|
||||
StackOp::Pop {
|
||||
dst: args.next_wr()?,
|
||||
num: args.next_rd()?,
|
||||
}
|
||||
}
|
||||
|
||||
_other => {
|
||||
return Ok(ParseOpResult::Unknown(args));
|
||||
}
|
||||
}))))
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
crsn = { path = "../crsn" }
|
||||
crsn_arith = { path = "../crsn_arith" }
|
||||
crsn_stacks = { path = "../crsn_stacks" }
|
||||
|
||||
simple_logger = "1.9.0"
|
||||
log = "0.4.11"
|
||||
|
||||
+27
-6
@@ -4,9 +4,10 @@ extern crate log;
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
use crsn::asm::data::literal::Addr;
|
||||
use crsn::runtime::program::Program;
|
||||
|
||||
use crsn::runtime::run_thread::{RunThread, ThreadToken};
|
||||
use crsn_arith::ArithOpParser;
|
||||
use crsn_arith::ArithOps;
|
||||
use crsn_stacks::StackOps;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
@@ -42,7 +43,7 @@ fn main() {
|
||||
)
|
||||
";*/
|
||||
|
||||
let program = "
|
||||
/* let program = "
|
||||
(
|
||||
(main
|
||||
(j :lbl)
|
||||
@@ -63,15 +64,35 @@ fn main() {
|
||||
(ret r0)
|
||||
)
|
||||
)
|
||||
";*/
|
||||
|
||||
let program = "
|
||||
(
|
||||
(main
|
||||
(push 0 10)
|
||||
(push 0 20)
|
||||
(push 0 30)
|
||||
(pop r0 0)
|
||||
(pop r0 0)
|
||||
(pop r0 0)
|
||||
(halt)
|
||||
)
|
||||
)
|
||||
";
|
||||
|
||||
let parsers = vec![
|
||||
ArithOpParser::new()
|
||||
ArithOps::new(),
|
||||
StackOps::new(),
|
||||
];
|
||||
|
||||
let parsed = crsn::asm::assemble(program, parsers.as_slice()).unwrap();
|
||||
|
||||
let thread = RunThread::new(ThreadToken(0), parsed, Addr(0), &[]);
|
||||
let thread1 = RunThread::new(ThreadToken(0), parsed.clone(), Addr(0), &[]);
|
||||
let thread2 = RunThread::new(ThreadToken(1), parsed.clone(), Addr(0), &[]);
|
||||
|
||||
thread.start().join().unwrap();
|
||||
let a = thread1.start();
|
||||
let b = thread2.start();
|
||||
|
||||
a.join().unwrap();
|
||||
b.join().unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user