use std::fmt::Debug; use crate::asm::data::{ literal::DebugMsg, literal::Label, literal::RoutineName, Rd, Wr, }; use crate::asm::data::literal::Addr; use crate::asm::error::Error; use crate::asm::instr::Cond; use crate::asm::parse::arg_parser::ArgParser; use crate::runtime::fault::Fault; use crate::runtime::frame::{CallStack, StackFrame}; use crate::runtime::program::Program; /// A higher level simple opration #[derive(Debug)] pub enum Op { /// Do nothing Nop, /// Mark a jump target. Label(Label), /// Jump to a label Jump(Label), /// Jump to a label if a flag is set JumpIf(Cond, Label), /// Mark a far jump target (can be jumped to from another routine). /// This label is preserved in optimized code. FarLabel(Label), /// Jump to a label that can be in another function FarJump(Label), /// Call a routine with arguments. /// The arguments are passed as argX. Return values are stored in resX registers. Call(RoutineName, Vec), /// Exit the current routine with return values Ret(Vec), /// Mark a routine entry point (call target). /// Kept in the low level instruction file for position-independent code Routine(RoutineName), /// Skip backward or forward Skip(Rd), /// Skip if a flag is set SkipIf(Cond, Rd), /// Deny jumps, skips and run across this address, producing a run-time fault with a message. Barrier(Option), /// Generate a run-time fault with a debugger message Fault(Option), /// Copy value Move { dst: Wr, src: Rd }, /// Store runtime status to a register StoreStatus { dst: Wr }, /// Load runtime status from a register LoadStatus { src: Rd }, /// Instruction added by an extension Extension(Box), } pub trait OpTrait: Debug + Send + Sync + 'static { fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result; } pub enum ParseOpResult { Parsed(Box), Unknown(ArgParser), } pub trait AsmModule: Debug + Send + 'static { /// Get name of the module fn name(&self) -> &'static str; /// Parse an op. /// If the keyword matches and the function decides to parse the instruction, it must consume /// the argument list and either return Ok or Err. /// /// If the instruction keyword is not recognized, return Unknown with the unchanged argument list. fn parse_op(&self, keyword: &str, arg_tokens: ArgParser) -> Result; } pub type CyclesSpent = usize; #[derive(Debug)] pub struct EvalRes { pub cycles: CyclesSpent, pub advance: i64, } impl Default for EvalRes { fn default() -> Self { Self { cycles: 1, advance: 1, } } } impl OpTrait for Op { fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result { let mut res = EvalRes::default(); match self { Op::Nop => {} Op::Label(_) | Op::FarLabel(_) | Op::Routine(_) => { /* this is nop, but without any cost - just markers */ res.cycles = 0; } Op::Barrier(msg) => { return Err(Fault::Barrier { msg: msg.clone().unwrap_or_else(|| "BARRIER".into()) }); } Op::Fault(msg) => { return Err(Fault::FaultInstr { msg: msg.clone().unwrap_or_else(|| "FAULT".into()) }); } Op::FarJump(name) => { match program.find_far_label(name) { Ok(pos) => { frame.pc = pos; } Err(e) => { return Err(e); } } } Op::Jump(name) => { match program.find_local_label(frame.pc, name) { Ok(pos) => { frame.pc = pos; } Err(e) => { return Err(e); } } } Op::JumpIf(cond, name) => { if frame.status.test(*cond) { match program.find_local_label(frame.pc, name) { Ok(pos) => { frame.pc = pos; } Err(e) => { return Err(e); } } } } Op::Call(name, args) => { match program.find_routine(&name) { Ok(pos) => { let mut values = Vec::with_capacity(args.len()); for arg in args { values.push(frame.read(*arg)?); } let mut frame2 = StackFrame::new(pos, &values); std::mem::swap(frame, &mut frame2); call_stack.push(frame2); res.advance = 0; } Err(e) => { return Err(e); } } } Op::Ret(retvals) => { match call_stack.pop() { Some(previous) => { let mut values = Vec::with_capacity(retvals.len()); for arg in retvals { values.push(frame.read(*arg)?); } *frame = previous; frame.set_retvals(&values); } None => { return Err(Fault::CallStackUnderflow); } } } Op::Skip(val) => { let steps = frame.read(*val)?; res.advance = i64::from_ne_bytes(steps.to_ne_bytes()); program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + res.advance) as u64))?; } Op::SkipIf(cond, val) => { if frame.status.test(*cond) { let steps = frame.read(*val)?; res.advance = i64::from_ne_bytes(steps.to_ne_bytes()); program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + res.advance) as u64))?; } } Op::Move { dst, src } => { frame.status.clear(); let val = frame.read(*src)?; frame.status.update(val); frame.write(*dst, val)?; } Op::StoreStatus { dst } => { let packed = frame.status.store(); frame.write(*dst, packed)?; } Op::LoadStatus { src } => { let x = frame.read(*src)?; frame.status.load(x); } Op::Extension(xop) => { xop.execute(&program, call_stack, frame)?; } } Ok(res) } }