re-arrange modules, add "plug-in" system so instructions can be defined in multiple crates

pull/21/head
Ondřej Hruška 3 years ago
parent 547beed847
commit f015104b95
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 73
      Cargo.lock
  2. 6
      Cargo.toml
  3. 11
      asm/Cargo.toml
  4. 19
      asm/src/parse/parse_cond.rs
  5. 516
      asm/src/parse/parse_op.rs
  6. 22
      asm/src/parse/parse_routines.rs
  7. 11
      crsn/Cargo.toml
  8. 0
      crsn/src/asm/data/literal.rs
  9. 2
      crsn/src/asm/data/mask.rs
  10. 2
      crsn/src/asm/data/mod.rs
  11. 2
      crsn/src/asm/data/reg.rs
  12. 6
      crsn/src/asm/error.rs
  13. 3
      crsn/src/asm/instr/cond.rs
  14. 8
      crsn/src/asm/instr/flatten.rs
  15. 6
      crsn/src/asm/instr/mod.rs
  16. 53
      crsn/src/asm/instr/op.rs
  17. 17
      crsn/src/asm/mod.rs
  18. 15
      crsn/src/asm/parse/mod.rs
  19. 20
      crsn/src/asm/parse/parse_cond.rs
  20. 8
      crsn/src/asm/parse/parse_data.rs
  21. 17
      crsn/src/asm/parse/parse_instr.rs
  22. 109
      crsn/src/asm/parse/parse_op.rs
  23. 23
      crsn/src/asm/parse/parse_routines.rs
  24. 2
      crsn/src/asm/parse/sexp_expect.rs
  25. 0
      crsn/src/asm/patches/mod.rs
  26. 0
      crsn/src/asm/patches/sexp_is_a.rs
  27. 0
      crsn/src/asm/patches/try_remove.rs
  28. 8
      crsn/src/lib.rs
  29. 131
      crsn/src/runtime/exec/mod.rs
  30. 7
      crsn/src/runtime/fault.rs
  31. 9
      crsn/src/runtime/frame.rs
  32. 4
      crsn/src/runtime/frame/status.rs
  33. 6
      crsn/src/runtime/mlock.rs
  34. 3
      crsn/src/runtime/mod.rs
  35. 7
      crsn/src/runtime/program.rs
  36. 11
      crsn/src/runtime/run_thread.rs
  37. 2
      crsn/src/runtime/span.rs
  38. 0
      crsn/src/runtime/sparse.rs
  39. 7
      crsn_arith/Cargo.toml
  40. 28
      crsn_arith/src/defs.rs
  41. 166
      crsn_arith/src/exec.rs
  42. 6
      crsn_arith/src/lib.rs
  43. 452
      crsn_arith/src/parse.rs
  44. 16
      launcher/Cargo.toml
  45. 13
      launcher/src/main.rs

73
Cargo.lock generated

@ -6,15 +6,6 @@ version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
[[package]]
name = "asm"
version = "0.1.0"
dependencies = [
"anyhow",
"sexp",
"thiserror",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -65,13 +56,48 @@ name = "crsn"
version = "0.1.0"
dependencies = [
"anyhow",
"asm",
"dyn-clonable",
"log",
"runtime",
"simple_logger",
"num-traits",
"sexp",
"thiserror",
]
[[package]]
name = "crsn_arith"
version = "0.1.0"
dependencies = [
"crsn",
"num-traits",
]
[[package]]
name = "dyn-clonable"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4"
dependencies = [
"dyn-clonable-impl",
"dyn-clone",
]
[[package]]
name = "dyn-clonable-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dyn-clone"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c53dc3a653e0f64081026e4bf048d48fec9fce90c66e8326ca7292df0ff2d82"
[[package]]
name = "hermit-abi"
version = "0.1.16"
@ -81,6 +107,18 @@ dependencies = [
"libc",
]
[[package]]
name = "launcher"
version = "0.1.0"
dependencies = [
"anyhow",
"crsn",
"crsn_arith",
"log",
"simple_logger",
"thiserror",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -139,17 +177,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "runtime"
version = "0.1.0"
dependencies = [
"anyhow",
"asm",
"log",
"num-traits",
"thiserror",
]
[[package]]
name = "sexp"
version = "1.1.4"

@ -1,6 +1,6 @@
[workspace]
members = [
"asm",
"runtime",
"crsn"
"launcher",
"crsn",
"crsn_arith",
]

@ -1,11 +0,0 @@
[package]
name = "asm"
version = "0.1.0"
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
edition = "2018"
publish = false
[dependencies]
sexp = "1.1.4"
thiserror = "1.0.20"
anyhow = "1.0.32"

@ -1,19 +0,0 @@
use sexp::Sexp;
use crate::error::Error;
use crate::instr::{Cond, Instr, cond};
use crate::parse::parse_instr::parse_instructions;
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::patches::TryRemove;
pub fn parse_cond_branch(tok: Sexp) -> Result<(Cond, Vec<Instr>), Error> {
let mut list = expect_list(Some(tok), false)?;
let kw = expect_string_atom(list.try_remove(0))?;
if !kw.ends_with('?') {
return Err(Error::Parse(format!("Condition must end with '?': {}", kw).into()));
}
Ok((cond::parse_cond(&kw)?, parse_instructions(list)?))
}

@ -1,516 +0,0 @@
use sexp::Sexp;
use crate::data::{Rd, Wr};
use crate::data::literal::{Label, RoutineName};
use crate::error::Error;
use crate::instr::{HLOp, Op};
use crate::instr::cond::parse_cond;
use crate::parse::parse_data::{parse_label, parse_rd, parse_wr};
use crate::parse::sexp_expect::expect_string_atom;
pub fn parse_op(keyword: &str, far: bool, mut arg_tokens: impl ExactSizeIterator<Item=Sexp> + Clone) -> Result<HLOp, Error> {
Ok(match keyword {
"j" => {
let dest = parse_label(arg_tokens.next())?;
if far {
HLOp::L(Op::FarJump(dest))
} else {
HLOp::Jump(dest)
}
}
"call" => {
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
let mut args = vec![];
for t in arg_tokens {
args.push(parse_rd(Some(t))?);
}
HLOp::L(Op::Call(dest, args))
}
"ret" => {
let mut args = vec![];
for t in arg_tokens {
args.push(parse_rd(Some(t))?);
}
HLOp::L(Op::Ret(args))
}
"routine" => {
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
HLOp::L(Op::Routine(dest))
}
"s" => {
HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?))
}
"sif" => {
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
let offs = parse_rd(arg_tokens.next())?;
HLOp::L(Op::SkipIf(cond, offs))
}
"jif" => {
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
let dest = parse_label(arg_tokens.next())?;
HLOp::JumpIf(cond, dest)
}
"barrier" => {
HLOp::L(Op::Barrier(match arg_tokens.next() {
None => None,
Some(s) => Some(expect_string_atom(Some(s))?.into()),
}))
}
"fault" => {
HLOp::L(Op::Fault(match arg_tokens.next() {
None => None,
Some(s) => Some(expect_string_atom(Some(s))?.into()),
}))
}
"ld" => {
HLOp::L(Op::Move {
dst: parse_wr(arg_tokens.next())?,
src: parse_rd(arg_tokens.next())?,
})
}
"cmp" => {
HLOp::L(Op::Compare {
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
})
}
"tst" => {
let arg = parse_rd(arg_tokens.next())?;
HLOp::L(Op::Test { a: arg })
}
"inc" => {
let dst = parse_wr(arg_tokens.next())?;
HLOp::L(Op::Add {
dst,
a: dst.as_rd(),
b: Rd::immediate(1),
})
}
"dec" => {
let dst = parse_wr(arg_tokens.next())?;
HLOp::L(Op::Sub {
dst,
a: dst.as_rd(),
b: Rd::immediate(1),
})
}
"add" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Add {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Add {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("Add requires 2 or 3 arguments".into()));
}
})
}
"sub" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Sub {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Sub {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("Sub requires 2 or 3 arguments".into()));
}
})
}
"mul" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Mul {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Mul {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("Mul requires 2 or 3 arguments".into()));
}
})
}
"divr" => {
HLOp::L(match arg_tokens.len() {
3 => {
let dst = parse_wr(arg_tokens.next())?;
let rem = parse_wr(arg_tokens.next())?;
let div = parse_rd(arg_tokens.next())?;
Op::Div {
dst,
rem,
a: dst.as_rd(),
div,
}
}
4 => {
Op::Div {
dst: parse_wr(arg_tokens.next())?,
rem: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
div: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("DivR requires 3 or 4 arguments".into()));
}
})
}
"div" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Div {
dst: parse_wr(arg_tokens.next())?,
rem: Wr::discard(),
a: parse_rd(arg_tokens.next())?,
div: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
let div = parse_rd(arg_tokens.next())?;
Op::Div {
dst,
rem: Wr::discard(),
a: dst.as_rd(),
div,
}
}
_ => {
return Err(Error::Parse("Div requires 2 or 3 arguments".into()));
}
})
}
"mod" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Mod {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
div: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
let div = parse_rd(arg_tokens.next())?;
Op::Mod {
dst,
a: dst.as_rd(),
div,
}
}
_ => {
return Err(Error::Parse("Mod requires 2 or 3 arguments".into()));
}
})
}
"and" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::And {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::And {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("And requires 2 or 3 arguments".into()));
}
})
}
"or" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Or {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Or {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("Or requires 2 or 3 arguments".into()));
}
})
}
"xor" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Xor {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
b: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Xor {
dst,
a: dst.as_rd(),
b: parse_rd(arg_tokens.next())?,
}
}
_ => {
return Err(Error::Parse("Xor requires 2 or 3 arguments".into()));
}
})
}
"cpl" => {
HLOp::L(match arg_tokens.len() {
2 => {
Op::Cpl {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Cpl {
dst,
a: dst.as_rd(),
}
}
_ => {
return Err(Error::Parse("Cpl requires 1 or 2 arguments".into()));
}
})
}
"rol" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Rol {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
n: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Rol {
dst,
a: dst.as_rd(),
n: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Rol {
dst,
a: dst.as_rd(),
n: Rd::immediate(1),
}
}
_ => {
return Err(Error::Parse("Rol requires 1, 2 or 3 arguments".into()));
}
})
}
"ror" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Ror {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
n: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Ror {
dst,
a: dst.as_rd(),
n: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Ror {
dst,
a: dst.as_rd(),
n: Rd::immediate(1),
}
}
_ => {
return Err(Error::Parse("Ror requires 1, 2 or 3 arguments".into()));
}
})
}
"lsl" | "asl" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Lsl {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
n: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Lsl {
dst,
a: dst.as_rd(),
n: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Lsl {
dst,
a: dst.as_rd(),
n: Rd::immediate(1),
}
}
_ => {
return Err(Error::Parse("Lsl requires 1, 2 or 3 arguments".into()));
}
})
}
"lsr" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Lsr {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
n: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Lsr {
dst,
a: dst.as_rd(),
n: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Lsr {
dst,
a: dst.as_rd(),
n: Rd::immediate(1),
}
}
_ => {
return Err(Error::Parse("Lsr requires 1, 2 or 3 arguments".into()));
}
})
}
"asr" => {
HLOp::L(match arg_tokens.len() {
3 => {
Op::Asr {
dst: parse_wr(arg_tokens.next())?,
a: parse_rd(arg_tokens.next())?,
n: parse_rd(arg_tokens.next())?,
}
}
2 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Asr {
dst,
a: dst.as_rd(),
n: parse_rd(arg_tokens.next())?,
}
}
1 => {
let dst = parse_wr(arg_tokens.next())?;
Op::Asr {
dst,
a: dst.as_rd(),
n: Rd::immediate(1),
}
}
_ => {
return Err(Error::Parse("Asr requires 1, 2 or 3 arguments".into()));
}
})
}
other => {
if let Some(label) = other.strip_prefix(':') {
let label = Label::Named(label.to_string());
if far {
HLOp::L(Op::FarLabel(label))
} else {
HLOp::Label(label)
}
} else {
return Err(Error::Parse(format!("Unknown instruction: {}", other).into()));
}
}
})
}

@ -1,22 +0,0 @@
use sexp::Sexp;
use crate::data::literal::RoutineName;
use crate::error::Error;
use crate::instr::Routine;
use crate::parse::parse_instr::parse_instructions;
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::patches::TryRemove;
pub fn parse_routines(routines: Vec<Sexp>) -> Result<Vec<Routine>, Error> {
let mut parsed = vec![];
for rt in routines {
let mut def = expect_list(Some(rt), false)?;
let name = expect_string_atom(def.try_remove(0))?;
let body = parse_instructions(def)?;
parsed.push(Routine {
name: RoutineName(name),
body,
})
}
Ok(parsed)
}

@ -3,13 +3,12 @@ name = "crsn"
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
publish = false
[dependencies]
asm = { path = "../asm" }
runtime = { path = "../runtime" }
simple_logger = "1.9.0"
log = "0.4.11"
sexp = "1.1.4"
thiserror = "1.0.20"
anyhow = "1.0.32"
dyn-clonable = "0.9.0"
log = "0.4.11"
num-traits = "0.2.12"

@ -1,6 +1,6 @@
//! Mask applied to a data source or destination
use crate::error::AsmError;
use crate::asm::error::AsmError;
/// Bit mask to apply to a value
#[derive(Debug, Clone, Copy, Eq, PartialEq)]

@ -6,7 +6,7 @@ use literal::Addr;
pub use mask::Mask;
pub use reg::Register;
use crate::data::literal::{as_signed, Value};
use crate::asm::data::literal::{as_signed, Value};
use super::error::AsmError;

@ -1,6 +1,6 @@
use std::fmt::{self, Display, Formatter};
use crate::error::Error;
use crate::asm::error::Error;
/// Register name
#[derive(Debug, Clone, Copy, Eq, PartialEq)]

@ -2,9 +2,9 @@ use std::borrow::Cow;
use thiserror::Error;
use crate::data::{Mask, Register};
use crate::data::literal::Label;
use crate::instr::Cond;
use crate::asm::data::{Mask, Register};
use crate::asm::data::literal::Label;
use crate::asm::instr::Cond;
/// csn_asm unified error type
#[derive(Error, Debug)]

@ -1,6 +1,7 @@
use std::fmt::{self, Display, Formatter};
use std::ops::Not;
use crate::error::Error;
use crate::asm::error::Error;
/// Condition flag
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]

@ -1,10 +1,10 @@
use std::collections::HashMap;
use std::sync::atomic::AtomicU32;
use crate::data::{Rd, SrcDisp};
use crate::data::literal::{Label, Value};
use crate::error::{AsmError, Error};
use crate::instr::{Cond, HLOp, Instr, Op, Routine};
use crate::asm::data::{Rd, SrcDisp};
use crate::asm::data::literal::{Label, Value};
use crate::asm::error::{AsmError, Error};
use crate::asm::instr::{Cond, HLOp, Instr, Op, Routine};
/// A trait for something that can turn into multiple instructions
pub trait Flatten {

@ -4,14 +4,14 @@ pub use flatten::lower;
pub use op::HLOp;
pub use op::Op;
use crate::data::literal::RoutineName;
use crate::asm::data::literal::RoutineName;
mod op;
pub mod op;
pub mod cond;
mod flatten;
/// A higher-level instruction
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone)]
pub struct Instr {
pub op: HLOp,
pub branches: Option<Vec<(Cond, Vec<Instr>)>>,

@ -1,13 +1,22 @@
use crate::data::{
use std::fmt::Debug;
use dyn_clonable::*;
use sexp::Sexp;
use crate::asm::data::{
literal::DebugMsg, literal::Label,
literal::RoutineName,
Rd,
Wr,
};
use crate::instr::Cond;
use crate::asm::error::Error;
use crate::asm::instr::Cond;
use crate::runtime::fault::Fault;
use crate::runtime::frame::{CallStack, StackFrame};
use crate::runtime::program::Program;
/// A higher level simple opration
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug)]
pub enum HLOp {
/// Mark a jump target.
Label(Label),
@ -20,7 +29,7 @@ pub enum HLOp {
}
/// A low level instruction
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug)]
pub enum Op {
/// Do nothing
Nop,
@ -45,32 +54,14 @@ pub enum Op {
Barrier(Option<DebugMsg>),
/// Generate a run-time fault with a debugger message
Fault(Option<DebugMsg>),
/// Copy value
Move { dst: Wr, src: Rd },
Test { a: Rd },
Compare { a: Rd, b: Rd },
/// Store runtime status to a register
StoreStatus { dst: Wr },
/// Load runtime status from a register
LoadStatus { src: Rd },
Add { dst: Wr, a: Rd, b: Rd },
Sub { dst: Wr, a: Rd, b: Rd },
Mul { dst: Wr, a: Rd, b: Rd },
Div { dst: Wr, rem: Wr, a: Rd, div: Rd },
// "Mod" is functionally equivalent to "Div" with the result discarded,
// but status flags are updated by the remainder
Mod { dst: Wr, a: Rd, div: Rd },
And { dst: Wr, a: Rd, b: Rd },
Or { dst: Wr, a: Rd, b: Rd },
Xor { dst: Wr, a: Rd, b: Rd },
Cpl { dst: Wr, a: Rd },
Rol { dst: Wr, a: Rd, n: Rd },
// Rotate (with wrap-around)
Ror { dst: Wr, a: Rd, n: Rd },
Lsl { dst: Wr, a: Rd, n: Rd },
// Shift
Lsr { dst: Wr, a: Rd, n: Rd },
Asr { dst: Wr, a: Rd, n: Rd },
Extension(Box<dyn OpTrait>),
}
/// Make "into" work
@ -79,3 +70,13 @@ impl From<Op> for HLOp {
HLOp::L(op)
}
}
#[clonable]
pub trait OpTrait: Clone + Debug + Send + 'static {
fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<(), Fault>;
}
#[clonable]
pub trait OpParser: Clone + Debug + Send + 'static {
fn parse_op(&self, keyword: &str, far: bool, arg_tokens: Vec<Sexp>) -> Result<Box<dyn OpTrait>, Error>;
}

@ -1,4 +1,5 @@
use crate::instr::{lower, Op};
use crate::asm::instr::{lower, Op};
use crate::asm::instr::op::OpParser;
pub mod data;
pub mod error;
@ -7,8 +8,8 @@ pub mod parse;
pub mod patches;
/// Parse a program from string and assemble a low level instruction sequence from it.
pub fn assemble(source: &str) -> Result<Vec<Op>, error::Error> {
let parsed = parse::parse(source)?;
pub fn assemble(source: &str, parsers: &[Box<dyn OpParser>]) -> Result<Vec<Op>, error::Error> {
let parsed = parse::parse(source, parsers)?;
Ok(lower(parsed)?)
}
@ -16,11 +17,11 @@ pub fn assemble(source: &str) -> Result<Vec<Op>, error::Error> {
mod tests {
use std::sync::atomic::AtomicU32;
use crate::data::{DstDisp, Rd, Register, SrcDisp, Wr};
use crate::data::literal::{Addr, Label};
use crate::instr::{Flatten, HLOp, Instr, lower, Op};
use crate::instr::Cond;
use crate::parse::{parse, parse_instructions};
use crate::asm::data::{DstDisp, Rd, Register, SrcDisp, Wr};
use crate::asm::data::literal::{Addr, Label};
use crate::asm::instr::{Flatten, HLOp, Instr, lower, Op};
use crate::asm::instr::Cond;
use crate::asm::parse::{parse, parse_instructions};
#[test]
fn test_parse_empty() {

@ -3,21 +3,22 @@ use std::sync::atomic::AtomicU32;
pub use parse_instr::parse_instructions;
use parse_routines::parse_routines;
use crate::error::Error;
use crate::instr::{Flatten, HLOp, Routine};
use crate::parse::sexp_expect::expect_list;
use crate::asm::error::Error;
use crate::asm::instr::{Flatten, HLOp, Routine};
use crate::asm::instr::op::OpParser;
use crate::asm::parse::sexp_expect::expect_list;
mod parse_cond;
mod parse_instr;
mod parse_data;
pub mod parse_data;
mod parse_routines;
mod sexp_expect;
pub mod sexp_expect;
mod parse_op;
pub fn parse(source: &str) -> Result<Vec<HLOp>, Error> {
pub fn parse(source: &str, parsers: &[Box<dyn OpParser>]) -> Result<Vec<HLOp>, Error> {
let root = sexp::parse(source)?;
let subs: Vec<Routine> = parse_routines(expect_list(Some(root), true)?)?;
let subs: Vec<Routine> = parse_routines(expect_list(Some(root), true)?, parsers)?;
let mut combined = vec![];
let label_num = AtomicU32::new(0);

@ -0,0 +1,20 @@
use sexp::Sexp;
use crate::asm::error::Error;
use crate::asm::instr::{Cond, cond, Instr};
use crate::asm::instr::op::OpParser;
use crate::asm::parse::parse_instr::parse_instructions;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::TryRemove;
pub fn parse_cond_branch(tok: Sexp, parsers: &[Box<dyn OpParser>]) -> Result<(Cond, Vec<Instr>), Error> {
let mut list = expect_list(Some(tok), false)?;
let kw = expect_string_atom(list.try_remove(0))?;
if !kw.ends_with('?') {
return Err(Error::Parse(format!("Condition must end with '?': {}", kw).into()));
}
Ok((cond::parse_cond(&kw)?, parse_instructions(list, parsers)?))
}

@ -2,10 +2,10 @@ use std::convert::TryFrom;
use sexp::{Atom, Sexp};
use crate::data::{DataDisp, DstDisp, Rd, reg, SrcDisp, Wr};
use crate::data::literal::{Addr, Label};
use crate::error::Error;
use crate::parse::sexp_expect::expect_string_atom;
use crate::asm::data::{DataDisp, DstDisp, Rd, reg, SrcDisp, Wr};
use crate::asm::data::literal::{Addr, Label};
use crate::asm::error::Error;
use crate::asm::parse::sexp_expect::expect_string_atom;
/// Parse a label
pub fn parse_label(name: Option<Sexp>) -> Result<Label, Error> {

@ -1,14 +1,15 @@
use sexp::Sexp;
use crate::error::Error;
use crate::instr::Instr;
use crate::parse::parse_cond::parse_cond_branch;
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::patches::SexpIsA;
use crate::asm::error::Error;
use crate::asm::instr::Instr;
use crate::asm::instr::op::OpParser;
use crate::asm::parse::parse_cond::parse_cond_branch;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::SexpIsA;
use super::parse_op::parse_op;
pub fn parse_instructions(instrs: Vec<Sexp>) -> Result<Vec<Instr>, Error> {
pub fn parse_instructions(instrs: Vec<Sexp>, parsers: &[Box<dyn OpParser>]) -> Result<Vec<Instr>, Error> {
let mut parsed = vec![];
for expr in instrs {
let tokens = expect_list(Some(expr), false)?;
@ -32,7 +33,7 @@ pub fn parse_instructions(instrs: Vec<Sexp>) -> Result<Vec<Instr>, Error> {
let branches = {
let mut branches = vec![];
for t in branch_tokens {
branches.push(parse_cond_branch(t)?);
branches.push(parse_cond_branch(t, parsers)?);
}
if branches.is_empty() {
None
@ -42,7 +43,7 @@ pub fn parse_instructions(instrs: Vec<Sexp>) -> Result<Vec<Instr>, Error> {
};
parsed.push(Instr {
op: parse_op(name.as_str(), far, arg_tokens.into_iter())?,
op: parse_op(name.as_str(), far, arg_tokens, parsers)?,
branches,
});
}

@ -0,0 +1,109 @@
use sexp::Sexp;
use crate::asm::data::literal::{Label, RoutineName};
use crate::asm::error::{AsmError, Error};
use crate::asm::instr::{HLOp, Op};
use crate::asm::instr::cond::parse_cond;
use crate::asm::instr::op::OpParser;
use crate::asm::parse::parse_data::{parse_label, parse_rd, parse_wr};
use crate::asm::parse::sexp_expect::expect_string_atom;
pub fn parse_op(keyword: &str, far: bool, arg_tokens_slice: Vec<Sexp>, parsers: &[Box<dyn OpParser>]) -> Result<HLOp, Error> {
let mut arg_tokens = arg_tokens_slice.clone().into_iter();
Ok(match keyword {
"j" => {
let dest = parse_label(arg_tokens.next())?;
if far {
HLOp::L(Op::FarJump(dest))
} else {
HLOp::Jump(dest)
}
}
"call" => {
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
let mut args = vec![];
for t in arg_tokens {
args.push(parse_rd(Some(t))?);
}
HLOp::L(Op::Call(dest, args))
}
"ret" => {
let mut args = vec![];
for t in arg_tokens {
args.push(parse_rd(Some(t))?);
}
HLOp::L(Op::Ret(args))
}
"routine" => {
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
HLOp::L(Op::Routine(dest))
}
"s" => {
HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?))
}
"sif" => {
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
let offs = parse_rd(arg_tokens.next())?;
HLOp::L(Op::SkipIf(cond, offs))
}
"jif" => {
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
let dest = parse_label(arg_tokens.next())?;
HLOp::JumpIf(cond, dest)
}
"barrier" => {
HLOp::L(Op::Barrier(match arg_tokens.next() {
None => None,
Some(s) => Some(expect_string_atom(Some(s))?.into()),
}))
}
"fault" => {
HLOp::L(Op::Fault(match arg_tokens.next() {
None => None,
Some(s) => Some(expect_string_atom(Some(s))?.into()),
}))
}
"ld" => {
HLOp::L(Op::Move {
dst: parse_wr(arg_tokens.next())?,
src: parse_rd(arg_tokens.next())?,
})
}
other => {
if let Some(label) = other.strip_prefix(':') {
let label = Label::Named(label.to_string());
if far {
HLOp::L(Op::FarLabel(label))
} else {
HLOp::Label(label)
}
} else {
for p in parsers {
match p.parse_op(keyword, far, arg_tokens_slice.clone()) {
Ok(op) => return Ok(HLOp::L(Op::Extension(op))),
Err(Error::Asm(AsmError::UnknownInstruction)) => {
/* ok, try another */
}
Err(other) => {
return Err(other);
}
}
}
return Err(Error::Parse(format!("Unknown instruction: {}", other).into()));
}
}
})
}

@ -0,0 +1,23 @@
use sexp::Sexp;
use crate::asm::data::literal::RoutineName;
use crate::asm::error::Error;
use crate::asm::instr::op::OpParser;
use crate::asm::instr::Routine;
use crate::asm::parse::parse_instr::parse_instructions;
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
use crate::asm::patches::TryRemove;
pub fn parse_routines(routines: Vec<Sexp>, parsers: &[Box<dyn OpParser>]) -> Result<Vec<Routine>, Error> {
let mut parsed = vec![];
for rt in routines {
let mut def = expect_list(Some(rt), false)?;
let name = expect_string_atom(def.try_remove(0))?;
let body = parse_instructions(def, parsers)?;
parsed.push(Routine {
name: RoutineName(name),
body,
})
}
Ok(parsed)
}

@ -1,6 +1,6 @@
use sexp::{Atom, Sexp};
use crate::error::Error;
use crate::asm::error::Error;
pub fn expect_list(expr: Option<Sexp>, allow_empty: bool) -> Result<Vec<Sexp>, Error> {
if let Some(expr) = expr {

@ -0,0 +1,8 @@
#[macro_use]
extern crate log;
pub use sexp;
pub mod asm;
pub mod runtime;

@ -0,0 +1,131 @@
use crate::asm::data::literal::Addr;
use crate::asm::instr::Op;
use crate::runtime::fault::Fault;
use crate::runtime::frame::StackFrame;
use crate::runtime::run_thread::RunThread;
pub type CyclesSpent = usize;
pub struct EvalRes {
pub cycles: u8,
pub advance: i64,
}
impl RunThread {
// TODO unit tests
pub fn eval_op(&mut self) -> Result<EvalRes, Fault> {
let mut cycles = 1;
let mut advance = 1;
let frame = &mut self.frame;
let op = self.program.read(frame.pc);
debug!("------------------------");
debug!("{} | {:?}", frame.pc, op);
/* Operations can be given different execution times when run in slow mode. */
/* Presently, all that do anything use 1 cycle. */
match op {
Op::Nop => {}
Op::FarLabel(_) | Op::Routine(_) => {
/* this is nop, but without any cost - just markers */
cycles = 0;
}
Op::Barrier(msg) => {
return Err(Fault::Barrier {
msg: msg.clone().unwrap_or_else(|| "No msg".into())
});
}
Op::Fault(msg) => {
return Err(Fault::FaultInstr {
msg: msg.clone().unwrap_or_else(|| "No msg".into())
});
}
Op::FarJump(name) => {
debug!("Far jump to {}", name);
match self.program.find_far_label(name) {
Ok(pos) => {
debug!("label is at {}", pos);
self.frame.pc = pos;
}
Err(e) => {
return Err(e);
}
}
}
Op::Call(name, args) => {
debug!("Call routine {}", name);
match self.program.find_routine(name) {
Ok(pos) => {