osiris-set-std 0.1.17

A standard operation set.
Documentation
//! 0x02 Register operations (set, move, stack)
//! * `0x0201` [set_top]:target top:**32**
//! * `0x0202` [set_bottom]:target bottom:**32**
//! * `0x0203` [clear_range]
//! * `0x0204` [move_to_float]:float unsigned:**16** 
//! * `0x0205` [move_from_float]:unsigned float:**16**
//! * `0x0210` [push]:unsigned
//! * `0x0211` [pop]:unsigned
//! * TODO 0x0212 PUSH RANGE
//! * TODO 0x0213 POP RANGE
//! * TODO 0x0220 REVERSE RANGE
//! * TODO 0x0221 SWAP3 $start <$target> $end

use std::collections::HashMap;
use osiris_data::converters::Words;

use osiris_data::data::atomic::{HalfWord, Word};
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::floating_point::Number;
use osiris_process::register::RegisterId;

pub const SET_MASK: u16 = 0x0200;
pub const SET_TOP: OperationId = OperationId::new(SET_MASK | 0x01);
pub const SET_BOTTOM: OperationId = OperationId::new(SET_MASK | 0x02);
pub const CLEAR_RANGE: OperationId = OperationId::new(SET_MASK | 0x03);
pub const MOVE_TO_FLOAT: OperationId = OperationId::new(SET_MASK | 0x04);
pub const MOVE_FROM_FLOAT: OperationId = OperationId::new(SET_MASK | 0x05);
pub const PUSH: OperationId = OperationId::new(SET_MASK | 0x10);
pub const POP: OperationId = OperationId::new(SET_MASK | 0x11);
pub const GET_COMPARE: OperationId = OperationId::new(SET_MASK | 0x20);

/// # `0x0201` [set_top]:target top:**32**
///
/// ## Target
///  - The register to set
///
/// ## Arguments
///  - top
///
/// ## Operation
///  - `r:target.top = top`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::OneU32],
pub fn set_top(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let value = scheme.argument.get_one_u32()?;
    let bottom = cpu.bank_get(scheme.target).bottom();
    cpu.debug("merge", format!("{:08x}:{:08x}", value, bottom.to_u32()), "⚙️".to_string());
    cpu.bank_set(scheme.target, Word::merge(HalfWord::new(value), bottom));
    Ok(())
}

/// # `0x0202` [set_bottom]:target bottom:**32**
///
/// ## Target
///  - The register to set
///
/// ## Arguments
///  - 32 bits : the bottom of the 64 bits value
///
/// ## Operation
///  - sets `REG[$target].bottom = $argument`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::OneU32],
pub fn set_bottom(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let value = scheme.argument.get_one_u32()?;
    let top = cpu.bank_get(scheme.target).top();
    cpu.debug("merge", format!("{:08x}:{:08x}", top.to_u32(), value), "⚙️".to_string());
    cpu.bank_set(scheme.target, Word::merge(top, HalfWord::new(value)));
    Ok(())
}

/// `0x0203` [clear_range]
///
/// ## Target
///  - No target
///
/// ## Arguments
///  - Range($start, $end)
///
/// ## Operation
///  - sets `REG[$start..=$end] = 0`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::Range],
pub fn clear_range(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    for id in scheme.argument.get_range()?.to_usize_range() {
        let rid = RegisterId::new(id as u16);
        cpu.bank_set(rid, Word::default());
    }
    Ok(())
}

/// # `0x0204` [move_to_float]:float unsigned:**16** 
///
/// ## Target
///  - The vector RegisterId to write to.
///
/// ## Arguments
///  - 16 bits : nothing,
///  - 16 bits : the bank RegisterId to read from.
///
/// ## Operation
///  - sets `FLOAT[$target] = REG[$argument]`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::TwoU16],
pub fn move_to_float(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let (_, bank_id) = scheme.argument.get_two_u16()?;
    let word = cpu.bank_get(RegisterId::new(bank_id));
    cpu.vector_set(scheme.target, Number::from_word(word));
    Ok(())
}

/// # `0x0205` [move_from_float]:unsigned float:**16**
///
/// ## Target
///  - The bank RegisterId to write to.
///
/// ## Arguments
///  - 16 bits : nothing,
///  - 16 bits : the vector RegisterId to read from.
///
/// ## Operation
///  - sets `REG[$target] = FLOAT[$argument]`
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::TwoU16],
pub fn move_from_float(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    let (_, bank_id) = scheme.argument.get_two_u16()?;
    let number = cpu.vector_get(RegisterId::new(bank_id));
    cpu.bank_set(scheme.target, number.to_word());
    Ok(())
}

/// # `0x0210` [push]:unsigned
///
/// ## Target
///  - The bank register to read the value to push into the stack.
///
/// ## Arguments
/// - None
///
/// ## Operation
///  - push `REG[$target]` into the stack
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::NoArgument],
pub fn push(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    scheme.argument.get_no_argument()?;
    cpu.bank_push(scheme.target);
    Ok(())
}

/// # `0x0211` [pop]:unsigned
///
/// ## Target
///  - The bank register to write the value to pop from the stack.
///
/// ## Arguments
/// - None
///
/// ## Operation
///  - pop a value from the stack and put it into `REG[$target]`.
///
/// ## Errors
///  - [OperationError::InvalidArgumentType] if the provided argument is not an [ArgumentType::NoArgument],
pub fn pop(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    scheme.argument.get_no_argument()?;
    cpu.bank_pop(scheme.target);
    Ok(())
}

/// # `0x0220` [get_compare]:target
///
/// ## 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::NoArgument],
pub fn get_compare(cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<()> {
    scheme.argument.get_no_argument()?;
    cpu.bank_set(scheme.target, Words::from_i64(cpu.state.operation.compare));
    Ok(())
}

/// Returns register operations.
///
/// ## Operations
///
/// * `0x0201` [set_top]:target top:**32**
/// * `0x0202` [set_bottom]:target bottom:**32**
/// * `0x0203` [clear_range]
/// * `0x0204` [move_to_float]:float unsigned:**16** 
/// * `0x0205` [move_from_float]:unsigned float:**16**
/// * `0x0210` [push]:unsigned
/// * `0x0211` [pop]:unsigned
/// * `0x0220` [get_compare]:target
pub fn operation_set() -> OperationSet {
    let mut set: OperationSet = HashMap::new();
    set.insert(SET_TOP, Operation::new(SET_TOP, "set-top".to_string(), true, ArgumentType::OneU32, set_top));
    set.insert(SET_BOTTOM, Operation::new(SET_BOTTOM, "set-bottom".to_string(), true, ArgumentType::OneU32, set_bottom));
    set.insert(CLEAR_RANGE, Operation::new(CLEAR_RANGE, "clear-range".to_string(), false, ArgumentType::Range, clear_range));
    set.insert(MOVE_TO_FLOAT, Operation::new(MOVE_TO_FLOAT, "move-to-float".to_string(), true, ArgumentType::TwoU16, move_to_float));
    set.insert(MOVE_FROM_FLOAT, Operation::new(MOVE_FROM_FLOAT, "move-from-float".to_string(), true, ArgumentType::TwoU16, move_from_float));
    set.insert(PUSH, Operation::new(PUSH, "push".to_string(), true, ArgumentType::NoArgument, push));
    set.insert(POP, Operation::new(POP, "pop".to_string(), true, ArgumentType::NoArgument, pop));
    set.insert(GET_COMPARE, Operation::new(GET_COMPARE, "get-compare".to_string(), true, ArgumentType::NoArgument, get_compare));
    set
}