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.
202 lines
5.5 KiB
202 lines
5.5 KiB
use sexp::SourcePosition;
|
|
|
|
use crate::asm::data::{Rd, RdObj, Wr, RdWr};
|
|
use crate::asm::data::literal::{DebugMsg, Label, RoutineName, Value};
|
|
use crate::asm::instr::Op;
|
|
use crate::asm::instr::op::OpKind;
|
|
use std::fmt::{Display, Formatter};
|
|
use std::fmt;
|
|
use crate::asm::error::CrsnError;
|
|
|
|
|
|
#[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),
|
|
}
|
|
|
|
/// Instruction's bit mask
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct BitMask {
|
|
/// Operation bit width
|
|
pub width: u32,
|
|
/// Destination (or first operand's) offset
|
|
pub dst_pos: u32,
|
|
/// Source (or second operand's) offset
|
|
pub src_pos: u32,
|
|
/// Source 2 offset (if used)
|
|
pub src2_pos: u32,
|
|
}
|
|
|
|
impl Display for BitMask {
|
|
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 BitMask {
|
|
fn default() -> Self {
|
|
Self {
|
|
width: 64,
|
|
dst_pos: 0,
|
|
src_pos: 0,
|
|
src2_pos: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BitMask {
|
|
pub fn full() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn validate(&self, pos: &SourcePosition) -> Result<(), CrsnError> {
|
|
#[allow(non_snake_case)]
|
|
let MAXWIDTH = (std::mem::size_of::<Value>() as u32)*8;
|
|
|
|
if self.width == 0 || self.width > MAXWIDTH {
|
|
return Err(CrsnError::Parse(format!("Bit width must be 1-{}", MAXWIDTH).into(), pos.clone()));
|
|
}
|
|
|
|
// Validation
|
|
if self.src_pos + self.width > MAXWIDTH {
|
|
return Err(CrsnError::Parse("Invalid source bit mask".into(), pos.clone()));
|
|
}
|
|
if self.src2_pos + self.width > MAXWIDTH {
|
|
return Err(CrsnError::Parse("Invalid source 2 bit mask".into(), pos.clone()));
|
|
}
|
|
if self.dst_pos + self.width > MAXWIDTH {
|
|
return Err(CrsnError::Parse("Invalid destination bit mask".into(), pos.clone()));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
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 { proc: RoutineName, args: Vec<Rd> },
|
|
/// Spawn a coroutine. The invocation is similar to (call).
|
|
Spawn { handle: Wr, proc: RoutineName, args: 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),
|
|
/// Join a coroutine
|
|
Join(RdObj),
|
|
/// Yield control, optionally yielding a value that must be consumed (by reading the task handle)
|
|
/// before execution can resume
|
|
Yield { value: Option<Rd> },
|
|
/// Set runtime option
|
|
RuntimeOpt { opt: Rd, value: Rd },
|
|
/// Begin critical section
|
|
CriticalBegin,
|
|
/// End critical section
|
|
CriticalEnd,
|
|
/// 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: Wr, a: Rd, b: Rd, mask: BitMask },
|
|
/// 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, mask: BitMask },
|
|
/// Swap if equal to a pattern
|
|
CompareSwap { dst: RdWr, expected: Rd, src: Rd, mask: BitMask },
|
|
/// 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)
|
|
}
|
|
}
|
|
|