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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! 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 {
    NE,
    LT,
    LE,
    EQ,
    GE,
    GT,
}

impl Compare {
    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
}