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};
#[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(),
}
}
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 }) }
}
}
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 }) }
}
}
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) }
}
}
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 }) }
}
}
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 }) }
}
}
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
}
}