osiris_process/
operation.rs

1//! This module provides a way to define, parse and execute operations.
2//!
3//! **osiris_process** suggest this operation template :
4//! * **16-bits** : the operation identifier,
5//! * **16-bits** : a [RegisterId], the operation target, if any,
6//! * **32-bits** : the operation argument(s) (see [ArgumentScheme]).
7
8use std::collections::HashMap;
9use std::fmt::{Debug, Formatter};
10
11use osiris_data::data::atomic::Word;
12
13use crate::operation::error::OperationResult;
14use crate::operation::scheme::{ArgumentScheme, ArgumentType, InstructionScheme, OperationId};
15use crate::register::RegisterId;
16use crate::processor::Cpu;
17
18pub mod scheme;
19pub mod error;
20
21pub type OperationFn<T> = fn(&mut Cpu, InstructionScheme) -> OperationResult<T>;
22
23/// This struct represents an [Instruction] template.
24#[derive(Clone, Debug)]
25pub struct Operation<T> {
26    id: OperationId,
27    name: String,
28    has_target: bool,
29    argument: ArgumentType,
30    function: OperationFn<T>,
31}
32
33impl<T> Operation<T> {
34    pub fn new(id: OperationId, name: String, has_target: bool, argument: ArgumentType, function: OperationFn<T>) -> Self {
35        Operation { id, name, has_target, argument, function }
36    }
37    pub fn id(&self) -> OperationId {
38        self.id
39    }
40    pub fn name(&self) -> String { self.name.clone() }
41    pub fn argument_type(&self) -> ArgumentType { self.argument }
42    pub fn has_target(&self) -> bool { self.has_target }
43    
44    /// Returns the asm template in the form of : "instr:target arg"
45    pub fn to_asm_template(&self) -> String {
46        let mut target_asm = "".to_string();
47        if self.has_target {
48            target_asm = ":$target".to_string();
49        }
50        let mut argument_asm = self.argument.to_asm_template();
51        if !argument_asm.is_empty() {
52            argument_asm = " ".to_string();
53            argument_asm.push_str(self.argument.to_asm_template().as_str());
54        }
55        format!("{}{}{}", self.name, target_asm, argument_asm).trim().to_string()
56    }
57
58    /// Call the internal [OperationFn] and return its result.
59    /// 
60    /// The [Cpu] is modified according to the [Operation] behavior.
61    pub fn call(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
62        (self.function)(cpu, scheme)
63    }
64
65    /// Prints information about the current instruction the calls [self.call]. 
66    pub fn call_debug(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
67        let instr = scheme.identifier.to_u16();
68        let target = scheme.target.to_u16();
69        let arg = scheme.argument.to_u32();
70        println!("[\x1b[34m{:04x}\x1b[31m{:04x}\x1b[33m{:08x}\x1b[0m] {}", instr, target, arg, self.to_debug_asm(scheme));
71        (self.function)(cpu, scheme)
72    }
73
74    /// Decodes the scheme into a string to print into a terminal. 
75    pub fn to_debug_asm(&self, scheme: InstructionScheme) -> String {
76        let mut target_asm = "".to_string();
77        if self.has_target {
78            target_asm = format!("\x1b[0m:\x1b[31m{:04x}", scheme.target.to_u16());
79        }
80        let mut argument_asm = "".to_string();
81        if self.argument != ArgumentType::NoArgument {
82            argument_asm = format!(" \x1b[33m{}", scheme.argument.to_asm());
83        }
84        format!("\x1b[34m{}{}{}\x1b[0m", self.name, target_asm, argument_asm).trim().to_string()
85    }
86}
87
88#[derive(Copy, Clone, Eq, PartialEq)]
89pub struct Instruction {
90    raw: u64,
91}
92
93impl Debug for Instruction {
94    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95        write!(f, "Instruction({:016x})", self.raw)
96    }
97}
98
99impl Instruction {
100    pub fn new(raw: u64) -> Self { Self { raw } }
101    pub fn to_u64(&self) -> u64 { self.raw }
102    pub fn from_word(word: Word) -> Self { Self::new(word.to_u64()) }
103    pub fn to_word(&self) -> Word { Word::new(self.to_u64()) }
104    pub fn operation_to_instruction<T>(&self, operation: &Operation<T>) -> InstructionScheme { self.scheme(operation.argument_type()) }
105
106    pub fn identifier(&self) -> OperationId { OperationId::new((self.raw >> 48) as u16) }
107    pub fn target(&self) -> RegisterId { RegisterId::new((self.raw >> 32) as u16) }
108    pub fn argument(&self, arg_type: ArgumentType) -> ArgumentScheme { ArgumentScheme::from_type(arg_type, self.raw as u32) }
109    pub fn scheme(&self, argument: ArgumentType) -> InstructionScheme {
110        InstructionScheme { identifier: self.identifier(), target: self.target(), argument: self.argument(argument) }
111    }
112    pub fn matches<T>(&self, operation: &Operation<T>) -> bool {
113        self.identifier() == operation.id()
114    }
115}
116
117pub type OperationSet = HashMap<OperationId, Operation<()>>;
118
119pub fn match_operation(set: &OperationSet, instruction: Instruction) -> Option<Operation<()>> {
120    if set.contains_key(&instruction.identifier()) {
121        Some(set[&instruction.identifier()].clone())
122    } else {
123        None
124    }
125}
126
127pub fn merge(sets: &[OperationSet]) -> OperationSet {
128    let mut merged_set = OperationSet::default();
129    for set in sets {
130        for sub in set {
131            merged_set.insert(*sub.0, sub.1.clone());
132        }
133    }
134    merged_set
135}