osiris-set-std 0.1.17

A standard operation set.
Documentation
use std::collections::HashMap;
use osiris_data::converters::Words;
use osiris_data::data::atomic::Word;
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::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 NOT: OperationId = OperationId::new(SET_MASK | 0x01);
pub const OR: OperationId = OperationId::new(SET_MASK | 0x02);
pub const AND: OperationId = OperationId::new(SET_MASK | 0x03);
pub const XOR: OperationId = OperationId::new(SET_MASK | 0x04);
pub const NOR: OperationId = OperationId::new(SET_MASK | 0x05);
pub const NAND: OperationId = OperationId::new(SET_MASK | 0x06);
pub const NXOR: OperationId = OperationId::new(SET_MASK | 0x07);

/// # `0x0400` [is]:`CMP` `target:16`:`compare:16`
///
/// ## Target
///  - CMP : LT(<) / LE(<=) / NE(!=) / EQ(==) / GE(>=) / GT(>)
///
/// ## Arguments
/// - target:register
/// - compare:register
///
/// ## Operation
///  - `REG[$target] = CMP == $compare`
///
/// ## 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 = Words::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(())
}

/// # `0x0401` [not]:`target` 0:`origin:16`
///
/// ## Target
///  - The bank register to write the value of the compare register.
///
/// ## Arguments
/// - 0:`$origin`
///
/// ## Operation
///  - `REG[$target] = not REG[$origin]`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::TwoU16],
pub fn not(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let (_, origin) = scheme.argument.get_two_u16()?;
    let origin = RegisterId::new(origin);
    cpu.bank_set(scheme.target, Word::new(!cpu.bank_get(origin).to_u64()));
    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` [xor]:`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()),
            FALSE,
        ),
    );
    Ok(())
}

/// # `0x0405` [nor]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0x00... nor REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn nor(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(())
}

/// # `0x0406` [nand]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0x00... nand REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn nand(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(())
}

/// # `0x0407` [nxor]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result value.
///
/// ## Arguments
/// - The register range onto apply the operator.
///
/// ## Operation
///  - `REG[$target] = 0x00... nxor REG[start..end]...`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn nxor(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`
/// * `0x0401` [not]:`target` 0:`origin:16`
/// * `0x0402` [or]:`target` `[start:end]`
/// * `0x0403` [and]:`target` `[start:end]`
/// * `0x0404` [xor]:`target` `[start:end]`
/// * `0x0405` [nor]:`target` `[start:end]`
/// * `0x0406` [nand]:`target` `[start:end]`
/// * `0x0407` [nxor]:`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(NOT, Operation::new(NOT, "not".to_string(), true, ArgumentType::TwoU16, not));
    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.insert(XOR, Operation::new(XOR, "xor".to_string(), true, ArgumentType::Range, xor));
    set.insert(NOR, Operation::new(NOR, "nor".to_string(), true, ArgumentType::Range, nor));
    set.insert(NAND, Operation::new(NAND, "nand".to_string(), true, ArgumentType::Range, nand));
    set.insert(NXOR, Operation::new(NXOR, "nxor".to_string(), true, ArgumentType::Range, nxor));

    set
}