mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'lemu/src/executor/mod.rs')
-rw-r--r--lemu/src/executor/mod.rs244
1 files changed, 244 insertions, 0 deletions
diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs
new file mode 100644
index 0000000..1998d3f
--- /dev/null
+++ b/lemu/src/executor/mod.rs
@@ -0,0 +1,244 @@
+mod builder;
+use super::{
+ instructions::{DrawInstr, DrawInstruction, Flow, Instr, LInstruction},
+ memory::{LAddress, LRegistry, LVar},
+};
+pub use builder::ExecutorBuilderInternal;
+use fimg::Image;
+use std::{collections::VecDeque, io::Write, num::NonZeroUsize, pin::Pin};
+
+#[derive(Debug, Copy, Clone)]
+pub struct Display(pub usize);
+#[derive(Debug, Copy, Clone)]
+// negative means bank, positive means cell
+pub struct Memory(pub(crate) i8);
+impl Memory {
+ pub(crate) fn limit(self, i: usize) -> usize {
+ if self.0 < 0 {
+ i.min(BANK_SIZE)
+ } else {
+ i.min(CELL_SIZE)
+ }
+ }
+}
+pub const BANK_SIZE: usize = 512;
+pub const CELL_SIZE: usize = 64;
+
+#[derive(Copy, Clone)]
+pub struct Instruction(pub usize);
+
+impl std::fmt::Debug for Instruction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Instruction#{}", self.0)
+ }
+}
+
+#[derive(Debug)]
+pub enum PInstr<'s> {
+ Instr(Instr<'s>),
+ Draw(DrawInstr<'s>),
+ Code(String),
+ NoOp,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Limit {
+ /// limited to n
+ Limited(NonZeroUsize),
+ /// unlimited
+ Unlimited,
+}
+
+impl Limit {
+ /// panics if n != 0
+ pub fn limited(n: usize) -> Self {
+ Self::Limited(n.try_into().expect("nonzero"))
+ }
+}
+
+impl Limit {
+ pub(crate) const fn reached(self, n: usize) -> bool {
+ match self {
+ Self::Limited(v) => v.get() <= n,
+ Self::Unlimited => false,
+ }
+ }
+}
+
+/// One time use logic executor.
+pub struct Executor<'varnames, W: Write> {
+ /// if limited, will run n instructions before exiting.
+ pub instruction_limit: Limit,
+ /// if limtited, will loop(go from a end to the start) n times before exiting
+ /// both unlimited does not mean this function will never return;
+ /// a `Stop` instruction will break the loop.
+ pub iteration_limit: Limit,
+ pub(crate) inner: ExecutorContext<'varnames, W>,
+ /// gets pointed to by drawbuf
+ pub(crate) program: Pin<Box<[PInstr<'varnames>]>>,
+ /// Counter for the number of instructions we have run so far.
+ pub instructions_ran: usize,
+ /// Counter for the number of iterations we have run so far.
+ pub iterations: usize,
+}
+
+pub enum UPInstr<'s> {
+ Instr(Instr<'s>),
+ Draw(DrawInstr<'s>),
+ UnfinishedJump,
+ Code(String),
+ NoOp,
+}
+
+pub struct Drawing<'v> {
+ pub displays: Box<[fimg::Image<Vec<u8>, 4>]>,
+ /// points to `Executor.program`
+ pub buffer: VecDeque<*const DrawInstr<'v>>,
+}
+
+impl<'v> Drawing<'v> {
+ fn buffer(&mut self, i: &DrawInstr<'v>) {
+ self.buffer.push_back(i);
+ }
+}
+pub struct ExecutorContext<'varnames, W: Write> {
+ // maximum of 128 elements, so can use ~60KB
+ pub cells: Box<[[f64; CELL_SIZE]]>, // screw world cells
+ // maximum of 127 elements, so can use ~500KB
+ pub banks: Box<[[f64; BANK_SIZE]]>,
+ pub memory: LRegistry<'varnames>,
+ pub counter: usize,
+ pub display: Drawing<'varnames>,
+ pub output: Option<W>,
+}
+
+pub struct DisplayState {
+ pub color: (u8, u8, u8, u8),
+ pub stroke: f64,
+}
+
+impl DisplayState {
+ pub const fn col(&self) -> [u8; 4] {
+ [self.color.0, self.color.1, self.color.2, self.color.3]
+ }
+}
+
+impl Default for DisplayState {
+ fn default() -> Self {
+ Self {
+ color: Default::default(),
+ stroke: 1.0,
+ }
+ }
+}
+
+impl<'s, W: Write> ExecutorContext<'s, W> {
+ pub fn flush(&mut self, to: Display) {
+ let mut state = DisplayState::default();
+ // SAFETY: safe as long as the instruction isnt held too long
+ while let Some(d) = unsafe { self.display.buffer.pop_front().map(|v| &*v) } {
+ d.draw(
+ &mut self.memory,
+ &mut self.display.displays[to.0].as_mut(),
+ &mut state,
+ );
+ }
+ }
+
+ pub fn mem(&mut self, Memory(m): Memory) -> &mut [f64] {
+ if m < 0 {
+ let m = (m + 1).unsigned_abs() as usize;
+ &mut self.banks[m]
+ } else {
+ let m = m as usize;
+ &mut self.cells[m]
+ }
+ }
+
+ pub fn set(&mut self, a: LAddress<'s>, b: LAddress<'s>) -> bool {
+ self.memory.set(a, b)
+ }
+
+ pub fn get_mut(&mut self, a: LAddress<'s>) -> Option<&mut LVar<'s>> {
+ self.memory.get_mut(a)
+ }
+
+ // please verify n is valid
+ pub fn jump(&mut self, Instruction(n): Instruction) {
+ self.counter = n;
+ }
+
+ pub fn get(&self, a: LAddress<'s>) -> LVar<'s> {
+ self.memory.get(a)
+ }
+}
+
+/// Returned by the [`output`](Executor::output).function.
+pub struct Output<W: Write> {
+ /// Everything created by a `print` instruction.
+ pub output: Option<W>,
+ /// Logic displays that were drawn with `draw` instructions.
+ pub displays: Box<[Image<Vec<u8>, 4>]>,
+ /// Memory banks, written to with the `write`/`read` instructions
+ pub cells: Box<[[f64; CELL_SIZE]]>,
+ /// Memory cells, written to with the `write`/`read` instructions
+ pub banks: Box<[[f64; BANK_SIZE]]>,
+}
+
+impl<'s, W: Write> Executor<'s, W> {
+ /// Consume this executor, returning all output.
+ pub fn output(mut self) -> Output<W> {
+ for display in &mut *self.inner.display.displays {
+ // TODO make the instructions draw flipped-ly
+ display.flip_v();
+ }
+ Output {
+ output: self.inner.output,
+ displays: self.inner.display.displays,
+ cells: self.inner.cells,
+ banks: self.inner.banks,
+ }
+ }
+
+ /// # Safety
+ ///
+ /// `counter` *must* be in bounds.
+ unsafe fn run_current(&mut self) -> Flow {
+ // SAFETY: yee
+ match unsafe { self.program.get_unchecked(self.inner.counter) } {
+ PInstr::Instr(i) => {
+ // println!("run {i:?} ({:?})", self.inner.memory);
+ i.run(&mut self.inner)
+ }
+ PInstr::Draw(i) => {
+ self.inner.display.buffer(i);
+ Flow::Continue
+ }
+ _ => Flow::Continue,
+ }
+ }
+
+ /// Begin code execution.
+ pub fn run(&mut self) {
+ while !self.instruction_limit.reached(self.instructions_ran)
+ && !self.iteration_limit.reached(self.iterations)
+ {
+ // SAFETY: we have a check
+ match unsafe { self.run_current() } {
+ Flow::Continue => {}
+ Flow::Exit => break,
+ Flow::Stay => {
+ self.instructions_ran += 1;
+ continue;
+ }
+ };
+ self.instructions_ran += 1;
+ self.inner.counter += 1;
+ if self.inner.counter >= self.program.len() {
+ self.inner.counter = 0;
+ self.iterations += 1;
+ self.inner.memory.clear();
+ }
+ }
+ }
+}