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);
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(())
}
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(())
}
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(())
}
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(())
}
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(())
}
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(())
}
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(())
}
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(())
}
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
}