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.
233 lines
6.3 KiB
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)
|
|
}
|
|
}
|
|
|