Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'lemu/src/instructions/mod.rs')
| -rw-r--r-- | lemu/src/instructions/mod.rs | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs new file mode 100644 index 0000000..6724ed1 --- /dev/null +++ b/lemu/src/instructions/mod.rs @@ -0,0 +1,500 @@ +//! supported instrs +//! +//! ```text +//! jump +//! op +//! stop +//! end +//! set +//! read +//! write +//! print +//! packcolor +//! +//! 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, Frozen}; +use enum_dispatch::enum_dispatch; +pub use mop::MathOp1; +pub use mop2::MathOp2; +use std::{fmt, io::Write}; + +use crate::debug::{info::DebugInfo, printable::Printable}; + +use super::{ + executor::{ExecutorContext, Instruction}, + memory::{LAddress, LVar}, +}; + +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"] +#[derive(Default)] +pub enum Flow { + #[default] + Continue, + Stay, + Exit, +} + +#[enum_dispatch] +pub trait LInstruction: Printable { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow; +} + +#[derive(Debug, Copy, Clone)] +#[enum_dispatch(LInstruction)] +pub enum Instr { + Op2(Op2), + Jump(Jump), + AlwaysJump(AlwaysJump), + Set(Set), + Op1(Op1), + Read(io::Read), + Write(io::Write), + DrawFlush(draw::Flush), + DynJump(DynJump), + Print(io::Print), + Stop(Stop), + PackColor(PackColor), + Select(Select), + Noop(Noop), + End(End), +} + +impl Printable for Instr { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + match self { + Self::Op2(i) => i.print(info, f), + Self::Jump(i) => i.print(info, f), + Self::AlwaysJump(i) => i.print(info, f), + Self::Set(i) => i.print(info, f), + Self::Op1(i) => i.print(info, f), + Self::Read(i) => i.print(info, f), + Self::Write(i) => i.print(info, f), + Self::DrawFlush(i) => i.print(info, f), + Self::DynJump(i) => i.print(info, f), + Self::Print(i) => i.print(info, f), + Self::Stop(i) => i.print(info, f), + Self::End(i) => i.print(info, f), + Self::PackColor(i) => i.print(info, f), + Self::Select(i) => i.print(info, f), + Self::Noop(i) => i.print(info, f), + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Set { + pub(crate) from: LAddress, + pub(crate) to: LAddress, +} +impl LInstruction for Set { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + exec.set(self.from, self.to); + Flow::Continue + } +} + +impl Printable for Set { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "set {} {}", info[self.from], info[self.to]) + } +} + +macro_rules! op_enum { + ($v:vis enum $name:ident { + $($variant:ident),+ $(,)? + }) => { + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + $v enum $name { + $($variant),+ + } + + impl<'a> TryFrom<Token<'a>> for $name { + type Error = Token<'a>; + fn try_from(value: Token<'a>) -> Result<Self, Self::Error> { + match value { + $(Token::$variant => Ok(Self::$variant),)+ + v => Err(v) + } + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{}", Token::from(*self)) + } + } + + impl<'a> From<$name> for Token<'a> { + fn from(value: $name) -> Self { + match value { + $($name::$variant => Self::$variant,)+ + } + } + } + } +} +use op_enum; + +// not part of op_enum due to rem +macro_rules! op_impl { + ($name:ident, ptr type = $ptr:ty { $($own:ident => $fn:ident,)+ }) => { + impl $name { + pub const fn get_fn(self) -> $ptr { + match self { + $(Self::$own => $fn,)+ + } + } + } + + impl TryFrom<$ptr> for $name { + type Error = (); + fn try_from(f:$ptr) -> Result<Self, ()> { + match f { + $(f if f == $fn => Ok(Self::$own),)+ + _ => Err(()), + } + } + } + } +} +use op_impl; + +macro_rules! get_num { + ($x:expr) => { + match $x { + LVar::Num(x) => *x, + _ => return Default::default(), + } + }; +} +use get_num; + +#[derive(Debug, Copy, Clone)] +pub struct Op1 { + op: for<'v> fn(&LVar<'v>) -> f64, + x: LAddress, + out: LAddress, +} +impl Op1 { + pub(crate) const fn new(op: MathOp1, x: LAddress, out: LAddress) -> Self { + Self { + op: op.get_fn(), + x, + out, + } + } +} + +impl LInstruction for Op1 { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + let x = (self.op)(exec.get(self.x)); + *exec.get_mut(self.out) = LVar::Num(x); + Flow::Continue + } +} + +impl Printable for Op1 { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + let op = mop::MathOp1::try_from(self.op).unwrap(); + write!(f, "op {op} {} {}", info[self.out], info[self.x]) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Op2 { + op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> f64, + a: LAddress, + b: LAddress, + out: LAddress, +} +impl Op2 { + pub(crate) const fn new(op: MathOp2, a: LAddress, b: LAddress, out: LAddress) -> Self { + Self { + op: op.get_fn(), + a, + b, + out, + } + } +} + +impl LInstruction for Op2 { + #[inline] + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + let x = (self.op)(exec.get(self.a), exec.get(self.b)); + exec.memory[self.out] = LVar::Num(x); + Flow::Continue + } +} + +impl Printable for Op2 { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + let op = mop2::MathOp2::try_from(self.op).unwrap(); + write!( + f, + "op {op} {} {} {}", + info[self.out], info[self.a], info[self.b] + ) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct End {} + +impl LInstruction for End { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + exec.iterations += 1; + // SAFETY: if we exist, 0 exists. + unsafe { exec.jump(Instruction::new(0)) }; + Flow::Stay + } +} + +impl Printable for End { + fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "end") + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Noop {} + +impl LInstruction for Noop { + fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow { + Flow::Continue + } +} + +impl Printable for Noop { + fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "noop") + } +} + +#[derive(Debug, Copy, Clone)] +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 + } +} + +impl Printable for AlwaysJump { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "jump ")?; + match info.label(self.to) { + Some(l) => f.write_str(l)?, + None => write!(f, "{}", self.to.get())?, + } + write!(f, " always") + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Jump { + op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool, + pub(crate) to: Instruction, + a: LAddress, + b: LAddress, +} +impl Jump { + pub fn new(op: ConditionOp, to: Instruction, a: LAddress, b: LAddress) -> Self { + Self { + op: op.get_fn(), + to, + a, + b, + } + } +} + +impl LInstruction for Jump { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + if (self.op)(exec.get(self.a), exec.get(self.b)) { + exec.jump(self.to); + Flow::Stay + } else { + Flow::Continue + } + } +} + +impl Printable for Jump { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + let op = ConditionOp::try_from(self.op).unwrap(); + write!(f, "jump {op} ")?; + match info.label(self.to) { + Some(l) => f.write_str(l)?, + None => write!(f, "{}", self.to.get())?, + }; + write!(f, " {} {}", info[self.a], info[self.b]) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct DynJump { + pub to: LAddress, + pub proglen: usize, +} + +impl LInstruction for DynJump { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + if let &LVar::Num(n) = exec.get(self.to) { + let i = n.round() as usize; + if i < self.proglen { + // SAFETY: just checked bounds + exec.jump(unsafe { Instruction::new(i) }); + return Flow::Stay; + } + } + Flow::Continue + } +} + +impl Printable for DynJump { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "set @counter {}", info[self.to]) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct Stop {} +impl LInstruction for Stop { + fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow { + Flow::Exit + } +} + +impl Printable for Stop { + fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "stop") + } +} + +#[derive(Copy, Clone, Debug)] +pub struct PackColor { + pub out: LAddress, + pub r: LAddress, + pub g: LAddress, + pub b: LAddress, + pub a: LAddress, +} + +impl LInstruction for PackColor { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + macro_rules! num { + ($($n:ident)+) => { + ($(match exec.memory[self.$n] { + LVar::Num(n) => (n.clamp(0.0, 1.0) * 255.0) as u8, + _ => 255, + },)+) + }; + } + let (r, g, b, a) = num!(r g b a); + exec.memory[self.out] = LVar::from(fimg::Pack::pack(&[r, g, b, a]) as f64); + Flow::Continue + } +} + +impl Printable for PackColor { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "packcolor {} {} {} {} {}", + info[self.out], info[self.r], info[self.g], info[self.b], info[self.a] + ) + } +} +#[derive(Debug, Copy, Clone)] +pub struct Select { + pub op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool, + pub(crate) to: LAddress, + + pub x: LAddress, + pub y: LAddress, + + pub a: LAddress, + pub b: LAddress, +} + +impl LInstruction for Select { + fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + *exec.get_mut(self.to) = if (self.op)(exec.get(self.x), exec.get(self.y)) { + exec.get(self.a) + } else { + exec.get(self.b) + } + .clone(); + Flow::Continue + } +} + +impl Printable for Select { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "select {} {} {} {} {} {}", + info[self.to], + ConditionOp::try_from(self.op).unwrap(), + info[self.x], + info[self.y], + info[self.a], + info[self.b] + ) + } +} |