nes-sim 0.1.3

A NES (Famicom) emulator core library written in pure Rust.
Documentation
use crate::{ControllerState, FRAME_WIDTH};

pub const VIDEO_FRAME_PITCH: usize = FRAME_WIDTH;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PixelFormat {
    Indexed8,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VideoFrame<'a> {
    pub width: usize,
    pub height: usize,
    pub pitch: usize,
    pub format: PixelFormat,
    pub frame_number: u64,
    pub pixels: &'a [u8],
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AudioBatch<'a> {
    pub channels: u8,
    pub sample_rate: u32,
    pub samples: &'a [f32],
}

impl<'a> Default for AudioBatch<'a> {
    fn default() -> Self {
        Self {
            channels: 1,
            sample_rate: 44_100,
            samples: &[],
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CoreCommand {
    Reset,
    SetControllerState {
        port: usize,
        state: ControllerState,
    },
    RunFrame,
    StepCpuInstruction,
    #[cfg(feature = "debug")]
    AddBreakpoint(Breakpoint),
    #[cfg(feature = "debug")]
    RemoveBreakpoint(Breakpoint),
    #[cfg(feature = "debug")]
    SetPaused(bool),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CoreEvent {
    None,
    ResetComplete,
    ControllerStateUpdated { port: usize },
    FrameReady { frame_number: u64 },
    CpuInstructionComplete { instruction_counter: u64 },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CoreResponse {
    pub event: CoreEvent,
    pub master_clock: u64,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CpuDebugSnapshot {
    pub a: u8,
    pub x: u8,
    pub y: u8,
    pub sp: u8,
    pub pc: u16,
    pub status: u8,
    pub clocks: u64,
    pub cycles_remaining: u64,
    pub instruction_counter: u64,
    pub irq_pending: bool,
    pub nmi_line: bool,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PpuDebugSnapshot {
    pub frame: u64,
    pub scanline: i16,
    pub in_vblank: bool,
    pub nmi_line: bool,
    pub oam_addr: u8,
    #[cfg(feature = "debug")]
    pub cycles: u16,
    #[cfg(feature = "debug")]
    pub ctrl: u8,
    #[cfg(feature = "debug")]
    pub mask: u8,
    #[cfg(feature = "debug")]
    pub status: u8,
    #[cfg(feature = "debug")]
    pub fine_x: u8,
    #[cfg(feature = "debug")]
    pub vram_addr: u16,
    #[cfg(feature = "debug")]
    pub temp_vram_addr: u16,
    #[cfg(feature = "debug")]
    pub write_latch: bool,
    #[cfg(feature = "debug")]
    pub bg_on: bool,
    #[cfg(feature = "debug")]
    pub sprites_on: bool,
    #[cfg(feature = "debug")]
    pub rendering_on: bool,
    #[cfg(feature = "debug")]
    pub odd_frame: bool,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DebugSnapshot {
    pub master_clock: u64,
    pub cpu: CpuDebugSnapshot,
    pub ppu: PpuDebugSnapshot,
}

#[cfg(feature = "debug")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DisassembledInstruction {
    pub address: u16,
    pub bytes: [u8; 3],
    pub len: u8,
    pub mnemonic: String,
    pub operand: String,
}

#[cfg(feature = "debug")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DisassemblyResult {
    pub instructions: Vec<DisassembledInstruction>,
    pub pc_index: usize,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MemorySnapshot<'a> {
    pub ram: &'a [u8; 0x800],
    pub vram: &'a [u8; 0x1000],
    pub chr: &'a [u8; 0x2000],
    pub palette: &'a [u8; 0x20],
    pub oam: &'a [u8; 256],
}

#[cfg(feature = "debug")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Breakpoint {
    Address(u16),
    MemoryRead(u16),
    MemoryWrite(u16),
    PpuScanline(i16),
    Vblank,
}

#[cfg(feature = "debug")]
#[derive(Debug, Clone, Default)]
pub struct Debugger {
    breakpoints: Vec<Breakpoint>,
    paused: bool,
}

#[cfg(feature = "debug")]
impl Debugger {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn add_breakpoint(&mut self, bp: Breakpoint) {
        if !self.breakpoints.contains(&bp) {
            self.breakpoints.push(bp);
        }
    }

    pub fn remove_breakpoint(&mut self, bp: &Breakpoint) {
        self.breakpoints.retain(|b| b != bp);
    }

    pub fn clear_breakpoints(&mut self) {
        self.breakpoints.clear();
    }

    pub fn breakpoints(&self) -> &[Breakpoint] {
        &self.breakpoints
    }

    pub fn set_paused(&mut self, paused: bool) {
        self.paused = paused;
    }

    pub fn paused(&self) -> bool {
        self.paused
    }
}