Croissant Runtime
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
crsn/crsn/src/builtin/defs.rs

233 lines
6.3 KiB

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<Rd>),
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<Option<Self>, 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<Option<Self>, 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<Option<Self>, 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<u32> = 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<Rd>),
/// Exit the current routine with return values
Ret(Vec<Rd>),
/// 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<DebugMsg>,
},
/// Generate a run-time fault with a debugger message
Fault(Option<DebugMsg>),
/// 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<BuiltinOp> for OpKind {
fn from(bo: BuiltinOp) -> Self {
OpKind::BuiltIn(bo)
}
}