osiris-set-std 0.1.17

A standard operation set.
Documentation
//! 0x10 Integral arithmetic (+, -, /, *, rnd(), <=>)
//! * `0x1001` [sum_unsigned]:`target` `[start:end]`
//! * `0x1002` [product_unsigned]:`target` `[start:end]`
//! * `0x1003` [difference_unsigned]:`target` `[start:end]`
//! * `0x1004` [quotient_unsigned]:`target` `[start:end]`
//! * TODO ... RANDOM
//! * `0x100F` [compare_unsigned] `r1:r2`

use std::collections::HashMap;

use osiris_process::compare::Compare;
use osiris_process::operation::error::{OperationError, OperationResult};
use osiris_process::operation::{Operation, OperationSet};
use osiris_process::operation::scheme::{ArgumentType, InstructionScheme, OperationId};
use osiris_process::processor::Cpu;
use osiris_process::register::{RegisterBank, RegisterId};

use crate::range_applications::unsigned;

pub const SET_MASK: u16 = 0x1000;

pub const SUM_UNSIGNED: OperationId = OperationId::new(SET_MASK | 0x01);
pub const PRODUCT_UNSIGNED: OperationId = OperationId::new(SET_MASK | 0x02);
pub const DIFFERENCE_UNSIGNED: OperationId = OperationId::new(SET_MASK | 0x03);
pub const QUOTIENT_UNSIGNED: OperationId = OperationId::new(SET_MASK | 0x04);
pub const COMPARE_UNSIGNED: OperationId = OperationId::new(SET_MASK | 0x0F);

/// # `0x1001` [sum_unsigned]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result into
///
/// ## Arguments
///  - Range : the range to sum
///
/// ## Operation
///  - `REG[$target] = sum(REG[$start..=$end])`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range]
pub fn sum_unsigned(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    cpu.bank_apply(scheme.target, scheme.argument.get_range()?, unsigned::sum);
    Ok(())
}

/// # `0x1002` [product_unsigned]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result into
///
/// ## Arguments
///  - Range : the range to product
///
/// ## Operation
///  - `REG[$target] = product(REG[$start..=$end])`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range]
pub fn product_unsigned(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    cpu.bank_apply(scheme.target, scheme.argument.get_range()?, unsigned::product);
    Ok(())
}

/// # `0x1003` [difference_unsigned]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result into
///
/// ## Arguments
///  - Range : the range to sum
///
/// ## Operation
///  - `REG[$target] = difference(REG[$start..=$end])`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range]
pub fn difference_unsigned(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    cpu.bank_apply(scheme.target, scheme.argument.get_range()?, unsigned::difference);
    Ok(())
}

/// # `0x1004` [quotient_unsigned]:`target` `[start:end]`
///
/// ## Target
///  - The register to store the result into
///
/// ## Arguments
///  - Range : the range to product
///
/// ## Operation
///  - `REG[$target] = quotient(REG[$start..=$end])`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range]
pub fn quotient_unsigned(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    cpu.bank_apply(scheme.target, scheme.argument.get_range()?, unsigned::quotient);
    Ok(())
}

/// # `0x100F` [compare_unsigned] `r1:r2`
/// 
/// ## Target
///  - None
///
/// ## Arguments
///  - Two registers ids.
///
/// ## Operation
///  - `REG[r1] <=> REG[r2]`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::TwoU16]
pub fn compare_unsigned(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let (a, b) = scheme.argument.get_two_u16()?;
    let a_val = cpu.state.bank.get(RegisterId::new(a));
    let b_val = cpu.state.bank.get(RegisterId::new(b));
    cpu.state.operation.compare = Compare::cmp(a_val, b_val);
    cpu.debug("compare", cpu.state.operation.compare.to_string(), "=".to_string());
    Ok(())
}

/// Returns integral arithmetic operations.
///
/// ## Operations
/// 
/// * `0x1001` [sum_unsigned]:`target` `[start:end]`
/// * `0x1002` [product_unsigned]:`target` `[start:end]`
/// * `0x1003` [difference_unsigned]:`target` `[start:end]`
/// * `0x1004` [quotient_unsigned]:`target` `[start:end]`
/// * `0x100F` [compare_unsigned] `r1:r2`
pub fn operation_set() -> OperationSet {
    let mut set: OperationSet = HashMap::new();
    set.insert(
        SUM_UNSIGNED,
        Operation::new(SUM_UNSIGNED, "sum-unsigned".to_string(), true, ArgumentType::Range, sum_unsigned),
    );
    set.insert(
        PRODUCT_UNSIGNED,
        Operation::new(PRODUCT_UNSIGNED, "product-unsigned".to_string(), true, ArgumentType::Range, product_unsigned),
    );
    set.insert(
        DIFFERENCE_UNSIGNED,
        Operation::new(DIFFERENCE_UNSIGNED, "difference-unsigned".to_string(), true, ArgumentType::Range, difference_unsigned),
    );
    set.insert(
        QUOTIENT_UNSIGNED,
        Operation::new(QUOTIENT_UNSIGNED, "quotient-unsigned".to_string(), true, ArgumentType::Range, quotient_unsigned),
    );
    set.insert(
        COMPARE_UNSIGNED,
        Operation::new(COMPARE_UNSIGNED, "compare-unsigned".to_string(), false, ArgumentType::TwoU16, compare_unsigned),
    );
    set
}