use sexp::SourcePosition; use crate::asm::data::{Rd, RdObj, Wr, RdWr}; use crate::asm::data::literal::{DebugMsg, Label, RoutineName}; use crate::asm::instr::Op; use crate::asm::instr::op::OpKind; use std::fmt::{Display, Formatter}; use std::fmt; use crate::asm::error::CrsnError; use crate::asm::patches::ErrWithPos; #[derive(Debug)] pub enum Barrier { /// Barrier that logically opens a section that cannot be jumped into, typically a routine Open(Label), /// Closing counterpart to the Open barrier Close(Label), /// Stand-alone barrier Standalone, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum SleepUnit { Usec, Msec, Sec } impl SleepUnit { pub fn micros(self) -> u64 { match self { SleepUnit::Usec => 1, SleepUnit::Msec => 1000, SleepUnit::Sec => 1000000, } } } #[derive(Clone, Debug, PartialEq)] pub enum LdsValue { Values(Vec), Handle(RdObj), Chars(String), } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct BitSlice { pub width: u32, pub src_pos: u32, pub dst_pos: u32 } impl Display for BitSlice { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if self.is_full() { return Ok(()); } write!(f, "{}", self.width)?; if self.src_pos != 0 || self.dst_pos != 0 { write!(f, "/{}", self.dst_pos)?; } if self.src_pos != 0 { write!(f, "/{}", self.src_pos)?; } Ok(()) } } impl Default for BitSlice { fn default() -> Self { Self { width: 64, src_pos: 0, dst_pos: 0 } } } impl BitSlice { pub fn full() -> Self { Self::default() } /// Parse LEN (no SRC and DST) pub fn parse1(src: &str, pos: &SourcePosition) -> Result, CrsnError> { match Self::parse(src, pos) { Ok(Some(slice)) => { if slice.src_pos != 0 || slice.dst_pos != 0 { return Err(CrsnError::Parse("Excess bit slice modifiers".into(), pos.clone())); } Ok(Some(BitSlice { width: slice.width, src_pos: 0, dst_pos: 0 })) } other => other } } /// Parse LEN/SRC pub fn parse2(src: &str, pos: &SourcePosition) -> Result, CrsnError> { match Self::parse(src, pos) { Ok(Some(slice)) => { if slice.src_pos != 0 { return Err(CrsnError::Parse("Excess bit slice modifiers".into(), pos.clone())); } Ok(Some(BitSlice { width: slice.width, src_pos: slice.dst_pos, dst_pos: 0 })) } other => other } } /// Parse a bit slice LEN/DST/SRC /// - String not starting with a digit is rejected as `Ok(None)` /// - Empty string is parsed as a full slice pub fn parse(src: &str, pos: &SourcePosition) -> Result, CrsnError> { if src.is_empty() { return Ok(Some(BitSlice::full())); } if !src.starts_with(|c: char| c.is_ascii_digit()) { return Ok(None); } // We have a opXX, opXX/YY, or opXX/YY/ZZ let mut numbers : Vec = vec![]; for p in src.split('/') { numbers.push(p.parse().err_pos(pos)?); } let slice = BitSlice { width: numbers[0], src_pos: numbers.get(2).copied().unwrap_or(0), dst_pos: numbers.get(1).copied().unwrap_or(0) }; if slice.width == 0 || slice.width > 64 { return Err(CrsnError::Parse("Bit slice width must be 1-64".into(), pos.clone())); } // Validation if slice.src_pos + slice.width > 64 { return Err(CrsnError::Parse("Invalid source bit slice".into(), pos.clone())); } if slice.dst_pos + slice.width > 64 { return Err(CrsnError::Parse("Invalid destination bit slice".into(), pos.clone())); } Ok(Some(slice)) } pub fn is_full(self) -> bool { self.width == 64 } } #[derive(Debug)] pub enum BuiltinOp { /// Do nothing (costs one cycle) Nop, /// Stop execution Halt, /// Sleep Sleep { count: Rd, unit_us: SleepUnit, }, /// Mark a jump target. Label(Label), /// Jump to a label Jump(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). /// The RoutineName struct includes its arity Routine(RoutineName), /// Skip backward or forward. The skip count can be defined by an argument. Skip(Rd), /// Deny jumps, skips and run across this address, producing a run-time fault. Barrier { kind: Barrier, msg: Option, }, /// Generate a run-time fault with a debugger message Fault(Option), /// Deallocate an extension object. /// The object is released and the handle becomes invalid. Delete(RdObj), /// Move a value Load { dst: Wr, src: Rd }, /// Move bits of a value LoadBits { dst: RdWr, src: Rd, slice: BitSlice }, /// Move N values LoadMultiple { dst: Wr, src: Rd, count: Rd }, /// Move values from a string or integer list LoadSequence { dst: Wr, value: LdsValue }, /// Swap two registers Exchange { a: RdWr, b: RdWr, slice : BitSlice }, /// Store runtime status to a register StoreFlags { dst: Wr }, /// Load runtime status from a register LoadFlags { src: Rd }, } impl BuiltinOp { pub fn into_op(self: BuiltinOp, pos: SourcePosition) -> Op { Op { kind: self.into(), pos, cond: None, } } } impl From for OpKind { fn from(bo: BuiltinOp) -> Self { OpKind::BuiltIn(bo) } }