osiris-process 0.2.4

A processor implementation.
Documentation
//! This module provides a way to define, parse and execute operations.
//!
//! **osiris_process** suggest this operation template :
//! * **16-bits** : the operation identifier,
//! * **16-bits** : a [RegisterId], the operation target, if any,
//! * **32-bits** : the operation argument(s) (see [ArgumentScheme]).

use std::collections::HashMap;
use std::fmt::{Debug, Formatter};

use osiris_data::data::atomic::Word;

use crate::operation::error::OperationResult;
use crate::operation::scheme::{ArgumentScheme, ArgumentType, InstructionScheme, OperationId};
use crate::processor::Cpu;
use crate::register::RegisterId;

pub mod scheme;
pub mod error;

#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Compare {
    LT,
    LE,
    NE,
    EQ,
    GE,
    GT,
}

impl Compare {
    
    /// Parses a `u16` to create a [Compare] value.
    /// 
    /// Returns [None] if `target_arg > 5`.
    pub fn from(target_arg: u16) -> Option<Self> {
        match target_arg { 
            0 => Some(Compare::LT),
            1 => Some(Compare::LE),
            2 => Some(Compare::NE),
            3 => Some(Compare::EQ),
            4 => Some(Compare::GE),
            5 => Some(Compare::GT),
            _ => None
        }
    }
    
    pub fn to_u16(&self) -> u16 {
        match self {
            Compare::LT => 0,
            Compare::LE => 1,
            Compare::NE => 2,
            Compare::EQ => 3,
            Compare::GE => 4,
            Compare::GT => 5,
        }
    }
    
    pub fn cmp(a: Word, b: Word) -> i64 {
        match a {
            a if a > b => 1,
            a if a < b => -1,
            _ => 0
        }
    }

    pub fn is(&self, cmp: i64) -> bool {
        match self {
            Compare::EQ => matches!(cmp, 0),
            Compare::LT => matches!(cmp, -1),
            Compare::GT => matches!(cmp, 1),
            Compare::LE => matches!(cmp, -1 | 0),
            Compare::NE => matches!(cmp, -1 | 1),
            Compare::GE => matches!(cmp, 0 | 1),
        }
    }

    pub fn check(&self, a: Word, b: Word) -> bool { self.is(Self::cmp(a, b)) }
}

pub type OperationFn<T> = fn(&mut Cpu, InstructionScheme) -> OperationResult<T>;

/// This struct represents an [Instruction] template.
#[derive(Clone, Debug)]
pub struct Operation<T> {
    id: OperationId,
    name: String,
    has_target: bool,
    argument: ArgumentType,
    function: OperationFn<T>,
}

impl<T> Operation<T> {
    pub fn new(id: OperationId, name: String, has_target: bool, argument: ArgumentType, function: OperationFn<T>) -> Self {
        Operation { id, name, has_target, argument, function }
    }
    pub fn id(&self) -> OperationId {
        self.id
    }
    pub fn name(&self) -> String { self.name.clone() }
    pub fn argument_type(&self) -> ArgumentType { self.argument }
    pub fn has_target(&self) -> bool { self.has_target }
    pub fn to_asm_template(&self) -> String {
        let mut target_asm = "".to_string();
        if self.has_target {
            target_asm = ":$target".to_string();
        }
        let mut argument_asm = self.argument.to_asm_template();
        if !argument_asm.is_empty() {
            argument_asm = " ".to_string();
            argument_asm.push_str(self.argument.to_asm_template().as_str());
        }
        format!("{}{}{}", self.name, target_asm, argument_asm).trim().to_string()
    }

    pub fn call(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
        (self.function)(cpu, scheme)
    }

    pub fn call_debug(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
        let instr = scheme.identifier.to_u16();
        let target = scheme.target.to_u16();
        let arg = scheme.argument.to_u32();
        println!("[\x1b[34m{:04x}\x1b[31m{:04x}\x1b[33m{:08x}\x1b[0m] {}", instr, target, arg, self.to_debug_asm(scheme));
        (self.function)(cpu, scheme)
    }

    pub fn to_debug_asm(&self, scheme: InstructionScheme) -> String {
        let mut target_asm = "".to_string();
        if self.has_target {
            target_asm = format!("\x1b[0m:\x1b[31m{:04x}", scheme.target.to_u16());
        }
        let mut argument_asm = "".to_string();
        if self.argument != ArgumentType::NoArgument {
            argument_asm = format!(" \x1b[33m{}", scheme.argument.to_asm());
        }
        format!("\x1b[34m{}{}{}\x1b[0m", self.name, target_asm, argument_asm).trim().to_string()
    }
}

#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Instruction {
    raw: u64,
}

impl Debug for Instruction {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Instruction({:016x})", self.raw)
    }
}

impl Instruction {
    pub fn new(raw: u64) -> Self { Self { raw } }
    pub fn to_u64(&self) -> u64 { self.raw }
    pub fn from_word(word: Word) -> Self { Self::new(word.to_u64()) }
    pub fn to_word(&self) -> Word { Word::new(self.to_u64()) }
    pub fn operation_to_instruction<T>(&self, operation: &Operation<T>) -> InstructionScheme { self.scheme(operation.argument_type()) }

    pub fn identifier(&self) -> OperationId { OperationId::new((self.raw >> 48) as u16) }
    pub fn target(&self) -> RegisterId { RegisterId::new((self.raw >> 32) as u16) }
    pub fn argument(&self, arg_type: ArgumentType) -> ArgumentScheme { ArgumentScheme::from_type(arg_type, self.raw as u32) }
    pub fn scheme(&self, argument: ArgumentType) -> InstructionScheme {
        InstructionScheme { identifier: self.identifier(), target: self.target(), argument: self.argument(argument) }
    }
    pub fn matches<T>(&self, operation: &Operation<T>) -> bool {
        self.identifier() == operation.id()
    }
}

pub type OperationSet = HashMap<OperationId, Operation<()>>;

pub fn match_operation(set: &OperationSet, instruction: Instruction) -> Option<Operation<()>> {
    if set.contains_key(&instruction.identifier()) {
        Some(set[&instruction.identifier()].clone())
    } else {
        None
    }
}

pub fn merge(sets: &[OperationSet]) -> OperationSet {
    let mut merged_set = OperationSet::default();
    for set in sets {
        for sub in set {
            merged_set.insert(*sub.0, sub.1.clone());
        }
    }
    merged_set
}