mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'lemu/src/instructions/mod.rs')
| -rw-r--r-- | lemu/src/instructions/mod.rs | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs new file mode 100644 index 0000000..514bc8e --- /dev/null +++ b/lemu/src/instructions/mod.rs @@ -0,0 +1,303 @@ +//! supported instrs +//! +//! ```text +//! jump +//! op +//! stop +//! end +//! set +//! read +//! write +//! print +//! +//! draw {color, col, flush, line, rect, lineRect, triangle, stroke, clear} +//! ``` +mod cop; +pub mod draw; +pub mod io; +mod mop; +mod mop2; + +pub use cop::ConditionOp; +pub use draw::{DrawInstr, DrawInstruction}; +use enum_dispatch::enum_dispatch; +pub use mop::MathOp1; +pub use mop2::MathOp2; +use std::io::Write; + +use super::{ + executor::{ExecutorContext, Instruction}, + memory::{LAddress, LVar}, +}; + +// pub const INSTRS: &[&str] = &[ +// "getlink", +// "read", +// "write", +// "set", +// "op", +// "end", +// "drawflush", +// "draw", +// "print", +// "packcolor", +// "jump", +// "stop", +// ]; + +pub const OPS: &[&str] = &[ + "equal", + "notEqual", + "lessThan", + "lessThanEq", + "greaterThan", + "greaterThanEq", + "strictEqual", + "always", + "add", + "sub", + "mul", + "div", + "idiv", + "mod", + "pow", + "land", + "not", + "shl", + "shr", + "or", + "and", + "xor", + "max", + "min", + "angle", + "angleDiff", + "len", + "noise", + "abs", + "log", + "log10", + "floor", + "ceil", + "sqrt", + "rand", + "sin", + "cos", + "tan", + "asin", + "acos", + "atan", +]; + +#[must_use = "to change control flow"] +pub enum Flow { + Continue, + Stay, + Exit, +} + +#[enum_dispatch] +pub trait LInstruction<'v> { + #[allow(unused_variables)] + fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { + Flow::Continue + } +} + +#[derive(Debug)] +#[enum_dispatch(LInstruction)] +pub enum Instr<'v> { + Op2(Op2<'v>), + Jump(Jump<'v>), + AlwaysJump(AlwaysJump), + Set(Set<'v>), + Op1(Op1<'v>), + Read(io::Read<'v>), + Write(io::Write<'v>), + DrawFlush(draw::Flush), + DynJump(DynJump<'v>), + Print(io::Print<'v>), + Stop(Stop), + End(End), +} + +#[derive(Debug)] +pub struct Set<'v> { + pub(crate) from: LAddress<'v>, + pub(crate) to: LAddress<'v>, +} +impl<'v> LInstruction<'v> for Set<'v> { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { + exec.set(self.from, self.to); + Flow::Continue + } +} + +macro_rules! op_enum { + ($v:vis enum $name:ident { + $($variant:ident),+ $(,)? + }) => { + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + $v enum $name { + $($variant),+ + } + + impl TryFrom<Token<'_>> for $name { + type Error = (); + fn try_from(value: Token<'_>) -> Result<Self, Self::Error> { + match value { + $(Token::$variant => Ok(Self::$variant),)+ + _ => Err(()) + } + } + } + } +} +use op_enum; + +macro_rules! get_num { + ($x:expr) => { + match $x { + LVar::Num(x) => x, + _ => return LVar::null(), + } + }; + ($x:expr, or ret) => { + match $x { + LVar::Num(x) => x, + _ => return, + } + }; +} +use get_num; + +#[derive(Debug)] +pub struct Op1<'v> { + pub(crate) op: fn(LVar<'v>) -> LVar<'v>, + pub(crate) x: LAddress<'v>, + pub(crate) out: LAddress<'v>, +} +impl<'v> Op1<'v> { + pub(crate) const fn new(op: MathOp1, x: LAddress<'v>, out: LAddress<'v>) -> Self { + Self { + op: op.get_fn(), + x, + out, + } + } +} + +impl<'s> LInstruction<'s> for Op1<'s> { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'s, W>) -> Flow { + let x = (self.op)(exec.get(self.x)); + if let Some(y) = exec.get_mut(self.out) { + *y = x; + } + Flow::Continue + } +} + +#[derive(Debug)] +pub struct Op2<'v> { + pub(crate) op: fn(LVar<'v>, LVar<'v>) -> LVar<'v>, + pub(crate) a: LAddress<'v>, + pub(crate) b: LAddress<'v>, + pub(crate) out: LAddress<'v>, +} +impl<'v> Op2<'v> { + pub(crate) const fn new( + op: MathOp2, + a: LAddress<'v>, + b: LAddress<'v>, + out: LAddress<'v>, + ) -> Self { + Self { + op: op.get_fn(), + a, + b, + out, + } + } +} + +impl<'v> LInstruction<'v> for Op2<'v> { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { + let x = (self.op)(exec.get(self.a), exec.get(self.b)); + if let Some(y) = exec.get_mut(self.out) { + *y = x; + } + Flow::Continue + } +} + +#[derive(Debug)] +pub struct End {} +impl LInstruction<'_> for End {} + +#[derive(Debug)] +pub struct AlwaysJump { + pub(crate) to: Instruction, +} +impl LInstruction<'_> for AlwaysJump { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + exec.jump(self.to); + Flow::Stay + } +} + +#[derive(Debug)] +pub struct Jump<'v> { + pub(crate) op: fn(LVar<'v>, LVar<'v>) -> bool, + pub(crate) to: Instruction, + pub(crate) a: LAddress<'v>, + pub(crate) b: LAddress<'v>, +} +impl<'v> Jump<'v> { + pub fn new(op: ConditionOp, to: Instruction, a: LAddress<'v>, b: LAddress<'v>) -> Self { + Self { + op: op.get_fn(), + to, + a, + b, + } + } +} + +#[derive(Debug)] +pub struct DynJump<'v> { + pub to: LAddress<'v>, + pub proglen: usize, +} + +impl<'v> LInstruction<'v> for DynJump<'v> { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { + if let LVar::Num(n) = exec.get(self.to) { + let i = n.round() as usize; + if i < self.proglen { + exec.jump(Instruction(i)); + return Flow::Stay; + } + } + Flow::Continue + } +} + +impl<'v> LInstruction<'v> for Jump<'v> { + #[allow(unused_variables)] + fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { + if (self.op)(exec.get(self.a), exec.get(self.b)) { + exec.jump(self.to); + Flow::Stay + } else { + Flow::Continue + } + } +} + +#[derive(Debug)] +pub struct Stop {} +impl LInstruction<'_> for Stop { + fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow { + Flow::Exit + } +} |