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
use std::collections::HashMap;
use osiris_data::data::atomic::{Word, WordTransmuter};
use osiris_process::compare::Compare;
use osiris_process::operation::{Operation, OperationSet};
use osiris_process::operation::error::{OperationError, OperationResult};
use osiris_process::operation::scheme::{ArgumentType, InstructionScheme, OperationId};
use osiris_process::processor::Cpu;
use osiris_process::register::RegisterId;
use crate::logic;
use crate::range_applications::reduce;

pub const TRUE: Word = Word::new(0xFFFFFFFFFFFFFFFF);
pub const FALSE: Word = Word::new(0x0);

pub const SET_MASK: u16 = 0x0400;

pub const IS: OperationId = OperationId::new(SET_MASK | 0x00);
pub const OR: OperationId = OperationId::new(SET_MASK | 0x02);
pub const AND: OperationId = OperationId::new(SET_MASK | 0x03);

/// # `0x0400` [is]:`CMP` `target:16`:`compare:16`
///
/// ## Target
///  - The bank register to write the value of the compare register.
///
/// ## Arguments
/// - None
///
/// ## Operation
///  - `REG[$target] = CMP`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::TwoU16],
pub fn is(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let (target, compare) = scheme.argument.get_two_u16()?;
    let compare = WordTransmuter::i64(cpu.bank_get(RegisterId::new(compare)));
    let cmp = Compare::from(scheme.target.to_u16());
    let result = cmp.unwrap().is(compare);
    cpu.bank_set(RegisterId::new(target), if result { TRUE } else { FALSE });
    Ok(())
}

/// # `0x0402` [or]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0x00... or REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn or(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let range = scheme.argument.get_range()?;
    cpu.bank_apply(
        scheme.target,
        range, 
        |slice | reduce(
            slice, 
            |carry, current| Word::new(carry.to_u64() | current.to_u64()),
            FALSE
        )
    );
    Ok(())
}

/// # `0x0403` [and]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0xFF... and REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn and(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let range = scheme.argument.get_range()?;
    cpu.bank_apply(
        scheme.target,
        range, 
        |slice | reduce(
            slice, 
            |carry, current| Word::new(carry.to_u64() & current.to_u64()),
            TRUE
        )
    );
    Ok(())
}

/// # `0x0404` [and]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0x00... xor REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn xor(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let range = scheme.argument.get_range()?;
    cpu.bank_apply(
        scheme.target,
        range, 
        |slice | reduce(
            slice, 
            |carry, current| Word::new(carry.to_u64() ^ current.to_u64()),
            TRUE
        )
    );
    Ok(())
}


/// Returns logic operations.
///
/// ## Operations
///
/// * `0x0400` [is]:`CMP` `target:16`:`compare:16`
/// * `0x0402` [or]:`target` `[start:end]`
/// * `0x0403` [and]:`target` `[start:end]`
pub fn operation_set() -> OperationSet {
    let mut set: OperationSet = HashMap::new();
    
    set.insert(IS, Operation::new(IS, "is".to_string(), true, ArgumentType::TwoU16, is));
    set.insert(OR, Operation::new(OR, "or".to_string(), true, ArgumentType::Range, or));
    set.insert(AND, Operation::new(AND, "and".to_string(), true, ArgumentType::Range, and));

    set
}