Skip to main content

lemu/instructions/
mod.rs

1//! supported instrs
2//!
3//! ```text
4//! jump
5//! op
6//! stop
7//! end
8//! set
9//! read
10//! write
11//! print
12//! packcolor
13//!
14//! draw {color, col, flush, line, rect, lineRect, triangle, stroke, clear}
15//! ```
16mod cop;
17pub mod draw;
18pub mod io;
19mod mop;
20mod mop2;
21
22pub use cop::ConditionOp;
23pub use draw::{DrawInstr, Frozen};
24use enum_dispatch::enum_dispatch;
25pub use mop::MathOp1;
26pub use mop2::MathOp2;
27use std::{fmt, io::Write};
28
29use crate::debug::{info::DebugInfo, printable::Printable};
30
31use super::{
32    executor::{ExecutorContext, Instruction},
33    memory::{LAddress, LVar},
34};
35
36pub const OPS: &[&str] = &[
37    "equal",
38    "notEqual",
39    "lessThan",
40    "lessThanEq",
41    "greaterThan",
42    "greaterThanEq",
43    "strictEqual",
44    "always",
45    "add",
46    "sub",
47    "mul",
48    "div",
49    "idiv",
50    "mod",
51    "pow",
52    "land",
53    "not",
54    "shl",
55    "shr",
56    "or",
57    "and",
58    "xor",
59    "max",
60    "min",
61    "angle",
62    "angleDiff",
63    "len",
64    "noise",
65    "abs",
66    "log",
67    "log10",
68    "floor",
69    "ceil",
70    "sqrt",
71    "rand",
72    "sin",
73    "cos",
74    "tan",
75    "asin",
76    "acos",
77    "atan",
78];
79
80#[must_use = "to change control flow"]
81#[derive(Default)]
82pub enum Flow {
83    #[default]
84    Continue,
85    Stay,
86    Exit,
87}
88
89#[enum_dispatch]
90pub trait LInstruction: Printable {
91    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow;
92}
93
94#[derive(Debug, Copy, Clone)]
95#[enum_dispatch(LInstruction)]
96pub enum Instr {
97    Op2(Op2),
98    Jump(Jump),
99    AlwaysJump(AlwaysJump),
100    Set(Set),
101    Op1(Op1),
102    Read(io::Read),
103    Write(io::Write),
104    DrawFlush(draw::Flush),
105    DynJump(DynJump),
106    Print(io::Print),
107    Stop(Stop),
108    PackColor(PackColor),
109    Select(Select),
110    Noop(Noop),
111    End(End),
112}
113
114impl Printable for Instr {
115    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
116        match self {
117            Self::Op2(i) => i.print(info, f),
118            Self::Jump(i) => i.print(info, f),
119            Self::AlwaysJump(i) => i.print(info, f),
120            Self::Set(i) => i.print(info, f),
121            Self::Op1(i) => i.print(info, f),
122            Self::Read(i) => i.print(info, f),
123            Self::Write(i) => i.print(info, f),
124            Self::DrawFlush(i) => i.print(info, f),
125            Self::DynJump(i) => i.print(info, f),
126            Self::Print(i) => i.print(info, f),
127            Self::Stop(i) => i.print(info, f),
128            Self::End(i) => i.print(info, f),
129            Self::PackColor(i) => i.print(info, f),
130            Self::Select(i) => i.print(info, f),
131            Self::Noop(i) => i.print(info, f),
132        }
133    }
134}
135
136#[derive(Debug, Copy, Clone)]
137pub struct Set {
138    pub(crate) from: LAddress,
139    pub(crate) to: LAddress,
140}
141impl LInstruction for Set {
142    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
143        exec.set(self.from, self.to);
144        Flow::Continue
145    }
146}
147
148impl Printable for Set {
149    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
150        write!(f, "set {} {}", info[self.from], info[self.to])
151    }
152}
153
154macro_rules! op_enum {
155    ($v:vis enum $name:ident {
156        $($variant:ident),+ $(,)?
157    }) => {
158        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
159        $v enum $name {
160            $($variant),+
161        }
162
163        impl<'a> TryFrom<Token<'a>> for $name {
164            type Error = Token<'a>;
165            fn try_from(value: Token<'a>) -> Result<Self, Self::Error> {
166                match value {
167                    $(Token::$variant => Ok(Self::$variant),)+
168                    v => Err(v)
169                }
170            }
171        }
172
173        impl std::fmt::Display for $name {
174            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
175                write!(f, "{}", Token::from(*self))
176            }
177        }
178
179        impl<'a> From<$name> for Token<'a> {
180            fn from(value: $name) -> Self {
181                match value {
182                    $($name::$variant => Self::$variant,)+
183                }
184            }
185        }
186    }
187}
188use op_enum;
189
190// not part of op_enum due to rem
191macro_rules! op_impl {
192    ($name:ident, ptr type = $ptr:ty { $($own:ident => $fn:ident,)+ }) => {
193        impl $name {
194            pub const fn get_fn(self) -> $ptr {
195                match self {
196                    $(Self::$own => $fn,)+
197                }
198            }
199        }
200
201        impl TryFrom<$ptr> for $name {
202            type Error = ();
203            fn try_from(f:$ptr) -> Result<Self, ()> {
204                match f {
205                    $(f if f == $fn => Ok(Self::$own),)+
206                    _ => Err(()),
207                }
208            }
209        }
210    }
211}
212use op_impl;
213
214macro_rules! get_num {
215    ($x:expr) => {
216        match $x {
217            LVar::Num(x) => *x,
218            _ => return Default::default(),
219        }
220    };
221}
222use get_num;
223
224#[derive(Debug, Copy, Clone)]
225pub struct Op1 {
226    op: for<'v> fn(&LVar<'v>) -> f64,
227    x: LAddress,
228    out: LAddress,
229}
230impl Op1 {
231    pub(crate) const fn new(op: MathOp1, x: LAddress, out: LAddress) -> Self {
232        Self {
233            op: op.get_fn(),
234            x,
235            out,
236        }
237    }
238}
239
240impl LInstruction for Op1 {
241    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
242        let x = (self.op)(exec.get(self.x));
243        *exec.get_mut(self.out) = LVar::Num(x);
244        Flow::Continue
245    }
246}
247
248impl Printable for Op1 {
249    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
250        let op = mop::MathOp1::try_from(self.op).unwrap();
251        write!(f, "op {op} {} {}", info[self.out], info[self.x])
252    }
253}
254
255#[derive(Debug, Copy, Clone)]
256pub struct Op2 {
257    op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> f64,
258    a: LAddress,
259    b: LAddress,
260    out: LAddress,
261}
262impl Op2 {
263    pub(crate) const fn new(op: MathOp2, a: LAddress, b: LAddress, out: LAddress) -> Self {
264        Self {
265            op: op.get_fn(),
266            a,
267            b,
268            out,
269        }
270    }
271}
272
273impl LInstruction for Op2 {
274    #[inline]
275    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
276        let x = (self.op)(exec.get(self.a), exec.get(self.b));
277        exec.memory[self.out] = LVar::Num(x);
278        Flow::Continue
279    }
280}
281
282impl Printable for Op2 {
283    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
284        let op = mop2::MathOp2::try_from(self.op).unwrap();
285        write!(
286            f,
287            "op {op} {} {} {}",
288            info[self.out], info[self.a], info[self.b]
289        )
290    }
291}
292
293#[derive(Debug, Copy, Clone)]
294pub struct End {}
295
296impl LInstruction for End {
297    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
298        exec.iterations += 1;
299        // SAFETY: if we exist, 0 exists.
300        unsafe { exec.jump(Instruction::new(0)) };
301        Flow::Stay
302    }
303}
304
305impl Printable for End {
306    fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
307        write!(f, "end")
308    }
309}
310
311#[derive(Debug, Copy, Clone)]
312pub struct Noop {}
313
314impl LInstruction for Noop {
315    fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow {
316        Flow::Continue
317    }
318}
319
320impl Printable for Noop {
321    fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
322        write!(f, "noop")
323    }
324}
325
326#[derive(Debug, Copy, Clone)]
327pub struct AlwaysJump {
328    pub(crate) to: Instruction,
329}
330impl LInstruction for AlwaysJump {
331    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
332        exec.jump(self.to);
333        Flow::Stay
334    }
335}
336
337impl Printable for AlwaysJump {
338    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
339        write!(f, "jump ")?;
340        match info.label(self.to) {
341            Some(l) => f.write_str(l)?,
342            None => write!(f, "{}", self.to.get())?,
343        }
344        write!(f, " always")
345    }
346}
347
348#[derive(Debug, Copy, Clone)]
349pub struct Jump {
350    op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool,
351    pub(crate) to: Instruction,
352    a: LAddress,
353    b: LAddress,
354}
355impl Jump {
356    pub fn new(op: ConditionOp, to: Instruction, a: LAddress, b: LAddress) -> Self {
357        Self {
358            op: op.get_fn(),
359            to,
360            a,
361            b,
362        }
363    }
364}
365
366impl LInstruction for Jump {
367    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
368        if (self.op)(exec.get(self.a), exec.get(self.b)) {
369            exec.jump(self.to);
370            Flow::Stay
371        } else {
372            Flow::Continue
373        }
374    }
375}
376
377impl Printable for Jump {
378    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
379        let op = ConditionOp::try_from(self.op).unwrap();
380        write!(f, "jump {op} ")?;
381        match info.label(self.to) {
382            Some(l) => f.write_str(l)?,
383            None => write!(f, "{}", self.to.get())?,
384        };
385        write!(f, " {} {}", info[self.a], info[self.b])
386    }
387}
388
389#[derive(Debug, Copy, Clone)]
390pub struct DynJump {
391    pub to: LAddress,
392    pub proglen: usize,
393}
394
395impl LInstruction for DynJump {
396    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
397        if let &LVar::Num(n) = exec.get(self.to) {
398            let i = n.round() as usize;
399            if i < self.proglen {
400                // SAFETY: just checked bounds
401                exec.jump(unsafe { Instruction::new(i) });
402                return Flow::Stay;
403            }
404        }
405        Flow::Continue
406    }
407}
408
409impl Printable for DynJump {
410    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
411        write!(f, "set @counter {}", info[self.to])
412    }
413}
414
415#[derive(Debug, Copy, Clone)]
416pub struct Stop {}
417impl LInstruction for Stop {
418    fn run<W: Write>(&self, _: &mut ExecutorContext<'_, W>) -> Flow {
419        Flow::Exit
420    }
421}
422
423impl Printable for Stop {
424    fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
425        write!(f, "stop")
426    }
427}
428
429#[derive(Copy, Clone, Debug)]
430pub struct PackColor {
431    pub out: LAddress,
432    pub r: LAddress,
433    pub g: LAddress,
434    pub b: LAddress,
435    pub a: LAddress,
436}
437
438impl LInstruction for PackColor {
439    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
440        macro_rules! num {
441            ($($n:ident)+) => {
442                ($(match exec.memory[self.$n] {
443                    LVar::Num(n) => (n.clamp(0.0, 1.0) * 255.0) as u8,
444                    _ => 255,
445                },)+)
446            };
447        }
448        let (r, g, b, a) = num!(r g b a);
449        exec.memory[self.out] = LVar::from(fimg::Pack::pack(&[r, g, b, a]) as f64);
450        Flow::Continue
451    }
452}
453
454impl Printable for PackColor {
455    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
456        write!(
457            f,
458            "packcolor {} {} {} {} {}",
459            info[self.out], info[self.r], info[self.g], info[self.b], info[self.a]
460        )
461    }
462}
463#[derive(Debug, Copy, Clone)]
464pub struct Select {
465    pub op: for<'v> fn(&LVar<'v>, &LVar<'v>) -> bool,
466    pub(crate) to: LAddress,
467
468    pub x: LAddress,
469    pub y: LAddress,
470
471    pub a: LAddress,
472    pub b: LAddress,
473}
474
475impl LInstruction for Select {
476    fn run<W: Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow {
477        *exec.get_mut(self.to) = if (self.op)(exec.get(self.x), exec.get(self.y)) {
478            exec.get(self.a)
479        } else {
480            exec.get(self.b)
481        }
482        .clone();
483        Flow::Continue
484    }
485}
486
487impl Printable for Select {
488    fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result {
489        write!(
490            f,
491            "select {} {} {} {} {} {}",
492            info[self.to],
493            ConditionOp::try_from(self.op).unwrap(),
494            info[self.x],
495            info[self.y],
496            info[self.a],
497            info[self.b]
498        )
499    }
500}