use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use osiris_data::data::atomic::Word;
use crate::operation::error::OperationResult;
use crate::operation::scheme::{ArgumentScheme, ArgumentType, InstructionScheme, OperationId};
use crate::register::RegisterId;
use crate::processor::Cpu;
pub mod scheme;
pub mod error;
pub type OperationFn<T> = fn(&mut Cpu, InstructionScheme) -> OperationResult<T>;
#[derive(Clone, Debug)]
pub struct Operation<T> {
id: OperationId,
name: String,
has_target: bool,
argument: ArgumentType,
function: OperationFn<T>,
}
impl<T> Operation<T> {
pub fn new(id: OperationId, name: String, has_target: bool, argument: ArgumentType, function: OperationFn<T>) -> Self {
Operation { id, name, has_target, argument, function }
}
pub fn id(&self) -> OperationId {
self.id
}
pub fn name(&self) -> String { self.name.clone() }
pub fn argument_type(&self) -> ArgumentType { self.argument }
pub fn has_target(&self) -> bool { self.has_target }
pub fn to_asm_template(&self) -> String {
let mut target_asm = "".to_string();
if self.has_target {
target_asm = ":$target".to_string();
}
let mut argument_asm = self.argument.to_asm_template();
if !argument_asm.is_empty() {
argument_asm = " ".to_string();
argument_asm.push_str(self.argument.to_asm_template().as_str());
}
format!("{}{}{}", self.name, target_asm, argument_asm).trim().to_string()
}
pub fn call(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
(self.function)(cpu, scheme)
}
pub fn call_debug(&self, cpu: &mut Cpu, scheme: InstructionScheme) -> OperationResult<T> {
let instr = scheme.identifier.to_u16();
let target = scheme.target.to_u16();
let arg = scheme.argument.to_u32();
println!("[\x1b[34m{:04x}\x1b[31m{:04x}\x1b[33m{:08x}\x1b[0m] {}", instr, target, arg, self.to_debug_asm(scheme));
(self.function)(cpu, scheme)
}
pub fn to_debug_asm(&self, scheme: InstructionScheme) -> String {
let mut target_asm = "".to_string();
if self.has_target {
target_asm = format!("\x1b[0m:\x1b[31m{:04x}", scheme.target.to_u16());
}
let mut argument_asm = "".to_string();
if self.argument != ArgumentType::NoArgument {
argument_asm = format!(" \x1b[33m{}", scheme.argument.to_asm());
}
format!("\x1b[34m{}{}{}\x1b[0m", self.name, target_asm, argument_asm).trim().to_string()
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Instruction {
raw: u64,
}
impl Debug for Instruction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Instruction({:016x})", self.raw)
}
}
impl Instruction {
pub fn new(raw: u64) -> Self { Self { raw } }
pub fn to_u64(&self) -> u64 { self.raw }
pub fn from_word(word: Word) -> Self { Self::new(word.to_u64()) }
pub fn to_word(&self) -> Word { Word::new(self.to_u64()) }
pub fn operation_to_instruction<T>(&self, operation: &Operation<T>) -> InstructionScheme { self.scheme(operation.argument_type()) }
pub fn identifier(&self) -> OperationId { OperationId::new((self.raw >> 48) as u16) }
pub fn target(&self) -> RegisterId { RegisterId::new((self.raw >> 32) as u16) }
pub fn argument(&self, arg_type: ArgumentType) -> ArgumentScheme { ArgumentScheme::from_type(arg_type, self.raw as u32) }
pub fn scheme(&self, argument: ArgumentType) -> InstructionScheme {
InstructionScheme { identifier: self.identifier(), target: self.target(), argument: self.argument(argument) }
}
pub fn matches<T>(&self, operation: &Operation<T>) -> bool {
self.identifier() == operation.id()
}
}
pub type OperationSet = HashMap<OperationId, Operation<()>>;
pub fn match_operation(set: &OperationSet, instruction: Instruction) -> Option<Operation<()>> {
if set.contains_key(&instruction.identifier()) {
Some(set[&instruction.identifier()].clone())
} else {
None
}
}
pub fn merge(sets: &[OperationSet]) -> OperationSet {
let mut merged_set = OperationSet::default();
for set in sets {
for sub in set {
merged_set.insert(*sub.0, sub.1.clone());
}
}
merged_set
}