1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! 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::register::RegisterId;
use crate::processor::Cpu;

pub mod scheme;
pub mod error;

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 }
    
    /// Returns the asm template in the form of : "instr:target arg"
    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()
    }

    /// Call the internal [OperationFn] and return its result.
    /// 
    /// The [Cpu] is modified according to the [Operation] behavior.
    pub fn call(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
        (self.function)(cpu, scheme)
    }

    /// Prints information about the current instruction the calls [self.call]. 
    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)
    }

    /// Decodes the scheme into a string to print into a terminal. 
    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
}