olympia_engine 0.3.0

Olympia is a gameboy emulator and toolkit, intended to run as a native or web assembly application targeting a cycle count accurate emulation. olympia_engine is the reusable core for both native and wasm.
Documentation
mod alu;
mod extended;
mod jump;
mod load;
pub(crate) mod misc;
mod stack;

use crate::disasm::Disassemble;
use crate::gameboy::StepResult;

use alloc::boxed::Box;
use alloc::vec::Vec;

use olympia_core::instructions::{Instruction, InstructionOpcode, SerializableInstruction};

pub trait ExecutableInstruction: Instruction {
    fn execute(&self, gb: &mut crate::gameboy::GameBoy) -> StepResult<()>;
}

pub trait RuntimeOpcode {
    fn to_instruction(&self, data: &mut dyn Iterator<Item = u8>) -> Box<dyn RuntimeInstruction>;
    fn all() -> Vec<(u8, Box<dyn RuntimeOpcode>)>
    where
        Self: Sized;
}

impl<T, F> RuntimeOpcode for T
where
    T: InstructionOpcode<FullInstruction = F> + 'static,
    F: RuntimeInstruction + 'static,
{
    fn to_instruction(&self, data: &mut dyn Iterator<Item = u8>) -> Box<dyn RuntimeInstruction> {
        Box::new(self.build_instruction(data))
    }

    fn all() -> Vec<(u8, Box<dyn RuntimeOpcode>)> {
        let mut output = vec![];
        for opcode in Self::definition().opcodes {
            let exe_opcode: Box<dyn RuntimeOpcode> = Box::new(Self::from_opcode(*opcode));
            output.push((*opcode, exe_opcode));
        }
        output
    }
}

pub trait RuntimeInstruction
where
    Self: ExecutableInstruction,
    Self: SerializableInstruction,
    Self: Disassemble,
{
}

impl<T> RuntimeInstruction for T
where
    T: ExecutableInstruction,
    T: SerializableInstruction,
    T: Disassemble,
{
}

pub struct RuntimeDecoder {
    opcodes: Vec<Option<Box<dyn RuntimeOpcode>>>,
    extended_opcodes: Vec<Option<Box<dyn RuntimeOpcode>>>,
}

impl RuntimeDecoder {
    pub fn new() -> RuntimeDecoder {
        let mut opcodes = Vec::with_capacity(256);
        let mut extended_opcodes = Vec::with_capacity(256);
        for _ in 0..256 {
            opcodes.push(None);
            extended_opcodes.push(None);
        }
        let input_codes = stack::opcodes()
            .into_iter()
            .chain(alu::opcodes())
            .chain(jump::opcodes())
            .chain(misc::opcodes())
            .chain(load::opcodes());

        for (value, executable) in input_codes {
            opcodes[value as usize] = Some(executable);
        }

        for (value, executable) in extended::opcodes() {
            extended_opcodes[value as usize] = Some(executable);
        }

        RuntimeDecoder {
            opcodes,
            extended_opcodes,
        }
    }

    pub fn is_extended(&self, value: u8) -> bool {
        value == 0xCB
    }

    pub fn decode(&self, value: u8) -> Option<&dyn RuntimeOpcode> {
        self.opcodes[value as usize].as_deref()
    }

    pub fn decode_from_iter(
        &self,
        val: u8,
        iter: &mut dyn Iterator<Item = u8>,
    ) -> Option<Box<dyn RuntimeInstruction>> {
        if self.is_extended(val) {
            iter.next()
                .map(|ex| self.decode_extended(ex).to_instruction(iter))
        } else if let Some(opcode) = self.decode(val) {
            Some(opcode.to_instruction(iter))
        } else {
            None
        }
    }

    pub fn decode_extended(&self, value: u8) -> &dyn RuntimeOpcode {
        self.extended_opcodes[value as usize].as_deref().unwrap()
    }
}

impl Default for RuntimeDecoder {
    fn default() -> RuntimeDecoder {
        RuntimeDecoder::new()
    }
}

#[cfg(test)]
mod alu_tests;

#[cfg(test)]
mod disassembly_tests;

#[cfg(test)]
mod extended_opcode_tests;

#[cfg(test)]
mod jump_tests;

#[cfg(test)]
mod interrupt_tests;

#[cfg(test)]
mod load_tests;

#[cfg(test)]
mod stack_tests;

#[cfg(test)]
mod misc_tests;