osiris-process 0.3.1

A processor implementation.
Documentation
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};

use osiris_data::data::identification::Identifier;

use crate::register::{RegisterId, RegisterRange};
use crate::operation::error::{OperationError, OperationResult};

/// Identifies a [crate::operation::Operation] in a [crate::operation::OperationSet]. 
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct OperationId(u16);

impl OperationId {
    pub const fn new(raw: u16) -> Self { Self(raw) }
    pub const fn to_u16(&self) -> u16 { self.0 }
}

impl Hash for OperationId {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_usize(self.to_usize());
    }
}

impl Identifier for OperationId {
    fn to_usize(&self) -> usize { self.0 as usize }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ArgumentType {
    NoArgument,
    Range,
    OneU32,
    TwoU16,
    FourU8,
}

impl ArgumentType {
    pub fn to_asm_template(&self) -> String {
        match self {
            ArgumentType::NoArgument => String::new(),
            ArgumentType::Range => "[{start}, {end}]".to_string(),
            ArgumentType::OneU32 => "{argument}".to_string(),
            ArgumentType::TwoU16 => "{first}, {second}".to_string(),
            ArgumentType::FourU8 => "{first}, {second}, {third}, {fourth}".to_string(),
        }
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ArgumentValue {
    OneU32(u32),
    TwoU16(u16, u16),
    FourU8(u8, u8, u8, u8),
}

impl ArgumentValue {
    pub fn to_u32(&self) -> u32 {
        match self {
            ArgumentValue::OneU32(v) => *v,
            ArgumentValue::TwoU16(t, b) => (*t as u32) << 8 | *b as u32,
            ArgumentValue::FourU8(a, b, c, d) => (*a as u32) << 24 | (*b as u32) << 16 | (*c as u32) << 8 | *d as u32,
        }
    }
}

impl Display for ArgumentValue {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", match self {
            ArgumentValue::OneU32(a) => format!("{:08x}", a),
            ArgumentValue::TwoU16(a, b) => format!("{:04x}:{:04x}", a, b),
            ArgumentValue::FourU8(a, b, c, d) => format!("{:02x}:{:02x}:{:02x}:{:02x}", a, b, c, d),
        })
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ArgumentScheme {
    NoArgument,
    Range(RegisterRange),
    Value(ArgumentValue),
}

impl ArgumentScheme {
    pub fn from_type(arg_type: ArgumentType, data: u32) -> ArgumentScheme {
        match arg_type {
            ArgumentType::NoArgument => ArgumentScheme::NoArgument,
            ArgumentType::Range => ArgumentScheme::Range(RegisterRange::from_u32(data)),
            ArgumentType::OneU32 => ArgumentScheme::Value(ArgumentValue::OneU32(data)),
            ArgumentType::TwoU16 => ArgumentScheme::Value(ArgumentValue::TwoU16((data >> 16) as u16, data as u16)),
            ArgumentType::FourU8 => ArgumentScheme::Value(ArgumentValue::FourU8((data >> 24) as u8, (data >> 16) as u8, (data >> 8) as u8, data as u8)),
        }
    }

    pub fn get_type(&self) -> ArgumentType {
        match self {
            ArgumentScheme::NoArgument => ArgumentType::NoArgument,
            ArgumentScheme::Value(a) => match a {
                ArgumentValue::OneU32(_) => ArgumentType::OneU32,
                ArgumentValue::TwoU16(_, _) => ArgumentType::TwoU16,
                ArgumentValue::FourU8(_, _, _, _) => ArgumentType::FourU8,
            },
            ArgumentScheme::Range(_) => ArgumentType::Range,
        }
    }

    pub fn to_u32(&self) -> u32 {
        match self {
            ArgumentScheme::NoArgument => 0,
            ArgumentScheme::Value(a) => a.to_u32(),
            ArgumentScheme::Range(range) => {
                let start = range.start.to_u16() as u32;
                let end = range.end.to_u16() as u32;
                (start << 16) | end
            }
        }
    }

    pub fn to_asm(&self) -> String {
        match self {
            ArgumentScheme::NoArgument => String::new(),
            ArgumentScheme::Value(value) => value.to_string(),
            ArgumentScheme::Range(range) => range.to_string(),
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_no_argument(&self) -> OperationResult<()> {
        match self {
            ArgumentScheme::NoArgument => { Ok(()) }
            ArgumentScheme::Value(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::NoArgument, provided: self.get_type() }) }
            ArgumentScheme::Range(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::NoArgument, provided: ArgumentType::Range }) }
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_value(&self) -> OperationResult<u32> {
        match self {
            ArgumentScheme::NoArgument => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::NoArgument }) }
            ArgumentScheme::Value(v) => { Ok(v.to_u32()) }
            ArgumentScheme::Range(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::Range }) }
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_range(&self) -> OperationResult<RegisterRange> {
        match self {
            ArgumentScheme::NoArgument => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::Range, provided: ArgumentType::NoArgument }) }
            ArgumentScheme::Value(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::Range, provided: ArgumentType::OneU32 }) }
            ArgumentScheme::Range(r) => { Ok(*r) }
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_one_u32(&self) -> OperationResult<u32> {
        match self {
            ArgumentScheme::NoArgument => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::NoArgument }) }
            ArgumentScheme::Value(v) => {
                match v {
                    ArgumentValue::OneU32(v) => Ok(*v),
                    ArgumentValue::TwoU16(_, _) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::TwoU16 }),
                    ArgumentValue::FourU8(_, _, _, _) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::FourU8 }),
                }
            }
            ArgumentScheme::Range(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::OneU32, provided: ArgumentType::Range }) }
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_two_u16(&self) -> OperationResult<(u16, u16)> {
        match self {
            ArgumentScheme::NoArgument => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::TwoU16, provided: ArgumentType::NoArgument }) }
            ArgumentScheme::Value(v) => {
                match v {
                    ArgumentValue::OneU32(_) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::TwoU16, provided: ArgumentType::OneU32 }),
                    ArgumentValue::TwoU16(a, b) => Ok((*a, *b)),
                    ArgumentValue::FourU8(_, _, _, _) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::TwoU16, provided: ArgumentType::FourU8 }),
                }
            }
            ArgumentScheme::Range(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::TwoU16, provided: ArgumentType::Range }) }
        }
    }

    /// Can return an [OperationError::InvalidArgumentType].
    pub fn get_four_u8(&self) -> OperationResult<(u8, u8, u8, u8)> {
        match self {
            ArgumentScheme::NoArgument => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::FourU8, provided: ArgumentType::NoArgument }) }
            ArgumentScheme::Value(v) => {
                match v {
                    ArgumentValue::OneU32(_) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::FourU8, provided: ArgumentType::OneU32 }),
                    ArgumentValue::TwoU16(_, _) => Err(OperationError::InvalidArgumentType { expected: ArgumentType::FourU8, provided: ArgumentType::TwoU16 }),
                    ArgumentValue::FourU8(a, b, c, d) => Ok((*a, *b, *c, *d)),
                }
            }
            ArgumentScheme::Range(_) => { Err(OperationError::InvalidArgumentType { expected: ArgumentType::FourU8, provided: ArgumentType::Range }) }
        }
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InstructionScheme {
    pub identifier: OperationId,
    pub target: RegisterId,
    pub argument: ArgumentScheme,
}

impl InstructionScheme {
    pub fn to_u64(&self) -> u64 {
        (self.identifier.to_u16() as u64) << 48
            | (self.target.to_u16() as u64) << 32
            | self.argument.to_u32() as u64
    }
}