Skip to content
Snippets Groups Projects
gb.rs 33.2 KiB
Newer Older
//! Main GameBoy emulation entrypoint functions and structures.

use std::{
    cell::RefCell,
    collections::VecDeque,
    fmt::{self, Display, Formatter},
    rc::Rc,
};

use crate::{
    cheats::{
        genie::{GameGenie, GameGenieCode},
        shark::{GameShark, GameSharkCode},
    },
    data::{BootRom, CGB_BOOT, DMG_BOOT, DMG_BOOTIX, MGB_BOOTIX, SGB_BOOT},
    devices::{printer::PrinterDevice, stdout::StdoutDevice},
    pad::{Pad, PadKey},
        Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB1555_SIZE,
        FRAME_BUFFER_RGB565_SIZE, FRAME_BUFFER_SIZE, FRAME_BUFFER_XRGB8888_SIZE,
    rom::{Cartridge, RamSize},
    serial::{NullDevice, Serial, SerialDevice},
    timer::Timer,
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

#[cfg(feature = "wasm")]
use crate::{
    gen::dependencies_map,
    ppu::{Palette, Pixel},
};
João Magalhães's avatar
João Magalhães committed

#[cfg(feature = "wasm")]
use std::{
    convert::TryInto,
    panic::{set_hook, take_hook, PanicInfo},
};
/// Enumeration that describes the multiple running
// modes of the Game Boy emulator.
// DMG = Original Game Boy
// CGB = Game Boy Color
// SGB = Super Game Boy
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum GameBoyMode {
impl GameBoyMode {
    pub fn description(&self) -> &'static str {
        match self {
            GameBoyMode::Dmg => "Game Boy (DMG)",
            GameBoyMode::Cgb => "Game Boy Color (CGB)",
            GameBoyMode::Sgb => "Super Game Boy (SGB)",
        }
    }
    pub fn from_u8(value: u8) -> Self {
        match value {
            1 => GameBoyMode::Dmg,
            2 => GameBoyMode::Cgb,
            3 => GameBoyMode::Sgb,
            _ => panic!("Invalid mode value: {}", value),
        }
    }

    pub fn from_string(value: &str) -> Self {
            "dmg" | "DMG" => GameBoyMode::Dmg,
            "cgb" | "CGB" => GameBoyMode::Cgb,
            "sgb" | "SGB" => GameBoyMode::Sgb,
            _ => panic!("Invalid mode value: {}", value),
        }
    }
    pub fn to_string(&self, uppercase: Option<bool>) -> String {
        let uppercase = uppercase.unwrap_or(false);
        match self {
            GameBoyMode::Dmg => (if uppercase { "DMG" } else { "dmg" }).to_string(),
            GameBoyMode::Cgb => (if uppercase { "CGB" } else { "cgb" }).to_string(),
            GameBoyMode::Sgb => (if uppercase { "SGB" } else { "sgb" }).to_string(),
        }
    }

    pub fn is_dmg(&self) -> bool {
        *self == GameBoyMode::Dmg
    }

    pub fn is_cgb(&self) -> bool {
        *self == GameBoyMode::Cgb
    }

    pub fn is_sgb(&self) -> bool {
        *self == GameBoyMode::Sgb
    }
}

impl Display for GameBoyMode {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum GameBoySpeed {
    Normal = 0,
    Double = 1,
}

impl GameBoySpeed {
    pub fn description(&self) -> &'static str {
        match self {
            GameBoySpeed::Normal => "Normal Speed",
            GameBoySpeed::Double => "Double Speed",
        }
    }

    pub fn switch(&self) -> Self {
        match self {
            GameBoySpeed::Normal => GameBoySpeed::Double,
            GameBoySpeed::Double => GameBoySpeed::Normal,
        }
    }

    pub fn multiplier(&self) -> u8 {
        match self {
            GameBoySpeed::Normal => 1,
            GameBoySpeed::Double => 2,
        }
    }

    pub fn from_u8(value: u8) -> Self {
        match value {
            0 => GameBoySpeed::Normal,
            1 => GameBoySpeed::Double,
            _ => panic!("Invalid speed value: {}", value),
        }
    }
}

impl Display for GameBoySpeed {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct GameBoyConfig {
    /// The current running mode of the emulator, this
    /// may affect many aspects of the emulation, like
    /// CPU frequency, PPU frequency, Boot rome size, etc.
    mode: GameBoyMode,
    /// If the PPU is enabled, it will be clocked.
    ppu_enabled: bool,

    /// If the APU is enabled, it will be clocked.
    apu_enabled: bool,
    /// if the DMA is enabled, it will be clocked.
    dma_enabled: bool,

    /// If the timer is enabled, it will be clocked.
    timer_enabled: bool,
    /// If the serial is enabled, it will be clocked.
    serial_enabled: bool,

    /// The current frequency at which the Game Boy
    /// emulator is being handled. This is a "hint" that
    /// may help components to adjust their internal
    /// logic to match the current frequency. For example
    /// the APU will adjust its internal clock to match
    /// this hint.
    clock_freq: u32,
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl GameBoyConfig {
    pub fn is_dmg(&self) -> bool {
        self.mode == GameBoyMode::Dmg
    }

    pub fn is_cgb(&self) -> bool {
        self.mode == GameBoyMode::Cgb
Loading
Loading full blame...