gpcas_forwardcom 0.2.0

ForwardCom instruction set architecture (ISA) properties for use with the General Purpose Core Architecture Simulator (GPCAS).
Documentation
// Filename: instructions.rs
// Author:	 Kai Rese
// Version:	 0.10
// Date:	 15-08-2022 (DD-MM-YYYY)
// Library:  gpcas_forwardcom
//
// Copyright (c) 2022 Kai Rese
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program. If not, see
// <https://www.gnu.org/licenses/>.

//! This module contains all instructions used by the emulator.
//!
//! It is important to note that not all properties are implemented yet, so the emulation relies on
//! a correctly assembled program. Most notably, instructions that ignore the mask field must not
//! set a mask.

mod functions;
mod jumps;
mod multi_format;
mod single_word;
mod three_words;
mod two_words;

use crate::emulator::core::{DecodedComplexInstruction, DecodedInstruction, Operand, OperandType};
use crate::emulator::{EmulatorInstruction, ForwardComEmulator, RegisterFile};

use gpcas_base::instruction_type;
use gpcas_isa::instruction_flags;
use std::collections::VecDeque;

pub use jumps::*;
pub use multi_format::*;
pub use single_word::*;
pub use three_words::*;
pub use two_words::*;

#[derive(Clone, Copy)]
/// Signals if an instruction operates on the entire vector or only on a single element.
///
/// Instruction operating on an entire vector must not have a mask.
pub enum VectorMode {
    /// The instruction only operates on the element pointed by an offset.
    Element,
    /// The instruction operates on the entire vector and therefore only needs to be called once.
    Full,
}

/// Represents execution functions for instructions.
///
/// They should roughly mirror what happens in the execution stage in a CPU.
pub type SimpleInstructionFunction =
    fn(emulator: &mut ForwardComEmulator, instruction: &mut EmulatorInstruction, offset: usize);

/// Instruction generation function type for complex multi-operation instructions.
pub type ComplexInstructionFunction = fn(
    instruction: DecodedComplexInstruction,
    register_file: &RegisterFile,
    memory: &[u8],
    output: &mut VecDeque<DecodedInstruction>,
);

/// Data that defines an ISA instruction that can be processed by the emulator.
pub struct SimpleIsaInstruction {
    /// The function that executes the instruction.
    pub function: SimpleInstructionFunction,
    /// How the instruction function operates on vectors.
    pub vector_mode: VectorMode,
    /// The number of input operands the instruction uses.
    pub inputs: u8,
    /// What execution port(s) the instruction needs and how it behaves in the ALU.
    ///
    /// See [`instruction_type`].
    pub instr_type: u16,
    /// A range of properties of the instruction.
    ///
    /// For more information, see [`instruction_flags`].
    pub instruction_flags: u16,
    /// If the instruction makes use of option bits, if present in the template.
    pub has_option_bits: bool,
    /// Overrides the data type.
    pub operand_type_override: Option<OperandType>,
    /// Overrides one operand type. Has the index of the operand and the new type.
    pub register_type_override: Option<(u8, Operand)>,
}

/// Data of an ISA instruction.
pub enum IsaInstruction {
    /// The instruction is a simple one that can be processed as-is.
    Simple(SimpleIsaInstruction),
    /// The instruction is a complex one that needs to be divided into multiple simpler ones.
    Complex(ComplexInstructionFunction),
}

/// An instruction that does nothing.
pub const NOP: SimpleIsaInstruction = SimpleIsaInstruction {
    function: |_, _, _| (),
    instruction_flags: 0,
    ..SimpleIsaInstruction::DEFAULT
};

/// An instruction that hasn't been implemented yet.
pub const NOT_YET_IMPLEMENTED_INSTRUCTION: SimpleIsaInstruction = SimpleIsaInstruction {
    function: |_, instruction, _| {
        log::debug!("Unimplemented instruction!");
        instruction.valid = false;
    },
    ..NOP
};

/// An instruction that isn't specified in the implemented ForwardCom specification.
pub const UNSPECIFIED_INSTRUCTION: SimpleIsaInstruction = SimpleIsaInstruction {
    function: |_, instruction, _| {
        log::debug!("Tried to use unspecified instruction!");
        instruction.valid = false;
    },
    ..NOP
};

/// The instruction has violated the index limit set by the format.
pub const LIMIT_VIOLATED: SimpleIsaInstruction = SimpleIsaInstruction {
    function: |_, instruction, _| {
        log::debug!("The index of the format went beyond the defined limit!");
        instruction.valid = false;
    },
    ..NOP
};

/// An instruction that doesn't execute in user mode.
pub const PRIVILEGED_INSTRUCTION: SimpleIsaInstruction = SimpleIsaInstruction {
    function: |_, instruction, _| {
        log::debug!("Tried to use privileged instruction in user mode!");
        instruction.valid = false;
    },
    ..NOP
};

impl SimpleIsaInstruction {
    /// The default value of an instruction.
    ///
    /// Is not implemented using the `Default` trait because that doesn't provide a constant value,
    /// but this is needed for the constant tables.
    /// Is not implemented using a constant function to mimic the `Default` trait because function
    /// pointers with mutable references as parameter aren't allowed in constant functions yet.
    const DEFAULT: Self = Self {
        function: |_, _, _| (),
        vector_mode: VectorMode::Element,
        inputs: 0,
        instr_type: instruction_type::REGISTER_MOVE,
        instruction_flags: instruction_flags::REG_OUT,
        has_option_bits: false,
        operand_type_override: None,
        register_type_override: None,
    };
}