Skip to main content

nes_sim/
lib.rs

1pub mod api;
2mod apu;
3mod bus;
4pub mod cartridge;
5mod cpu;
6mod dma;
7pub mod headless;
8mod input;
9mod ppu;
10mod ppu_memory;
11pub mod runtime;
12pub mod savestate;
13pub mod video;
14
15pub use api::{
16    AudioBatch, CoreCommand, CoreEvent, CoreResponse, CpuDebugSnapshot, DebugSnapshot, PixelFormat,
17    PpuDebugSnapshot, VIDEO_FRAME_PITCH, VideoFrame,
18};
19pub use apu::ExpansionAudioChip;
20pub use cartridge::{Cartridge, CartridgeError, Mirroring, TVSystem};
21pub use input::{ControllerButton, ControllerState};
22pub use ppu::{FRAME_HEIGHT, FRAME_WIDTH};
23pub use runtime::{
24    ExecutionTarget, FrontendInput, FrontendRuntime, RunMode, RuntimeSnapshot, RuntimeStatus,
25};
26pub use savestate::SaveStateError;
27use savestate::{StateReader, StateWriter};
28
29const NTSC_CPU_SCHEDULE: [u8; 1] = [3];
30const PAL_CPU_SCHEDULE: [u8; 5] = [3, 3, 3, 3, 4];
31const DENDY_CPU_SCHEDULE: [u8; 1] = [3];
32
33fn cpu_schedule(tv: TVSystem) -> &'static [u8] {
34    match tv {
35        TVSystem::NTSC => &NTSC_CPU_SCHEDULE,
36        TVSystem::PAL => &PAL_CPU_SCHEDULE,
37        TVSystem::DENDY => &DENDY_CPU_SCHEDULE,
38    }
39}
40
41pub struct NES {
42    cpu: cpu::CPU,
43    bus: bus::NESBus,
44    master_clock: u64,
45    cpu_ppu_counter: u8,
46    cpu_schedule_index: usize,
47    cached_tv_system: TVSystem,
48}
49
50impl NES {
51    pub fn new() -> Self {
52        Self {
53            cpu: cpu::CPU::new(),
54            bus: bus::NESBus::new(),
55            master_clock: 0,
56            cpu_ppu_counter: 0,
57            cpu_schedule_index: 0,
58            cached_tv_system: TVSystem::NTSC,
59        }
60    }
61
62    pub fn reset(&mut self) {
63        self.bus.reset();
64        self.reset_cpu_schedule();
65        self.cpu.reset(&mut self.bus);
66        self.cpu.set_nmi(self.bus.ppu_nmi_line());
67    }
68
69    pub fn insert_cartridge(&mut self, cartridge: Cartridge) {
70        self.bus.insert_cartridge(cartridge);
71        self.reset_cpu_schedule();
72        self.reset();
73    }
74
75    pub fn load_cartridge_ines(&mut self, rom: &[u8]) -> Result<(), CartridgeError> {
76        self.bus.load_cartridge_ines(rom)?;
77        self.reset_cpu_schedule();
78        Ok(())
79    }
80
81    pub fn set_controller_state(&mut self, port: usize, state: ControllerState) {
82        self.bus.set_controller_state(port, state);
83    }
84
85    pub fn clock(&mut self) {
86        self.master_clock += 1;
87        self.bus.tick_ppu();
88
89        let schedule = cpu_schedule(self.cached_tv_system);
90        self.cpu_ppu_counter += 1;
91        if self.cpu_ppu_counter >= schedule[self.cpu_schedule_index] {
92            self.cpu_ppu_counter = 0;
93            self.cpu_schedule_index = (self.cpu_schedule_index + 1) % schedule.len();
94            // Update NMI/IRQ only around CPU step, not every PPU cycle
95            self.cpu.set_nmi(self.bus.ppu_nmi_line());
96            self.bus.advance_dma_cpu_phase();
97            self.bus.tick_apu_cpu_cycle();
98            self.cpu.clock(&mut self.bus);
99            self.cpu.irq_set_level(0x01, self.bus.apu_irq_line());
100            self.cpu.irq_set_level(0x02, self.bus.cartridge_irq_line());
101            self.cpu.set_nmi(self.bus.ppu_nmi_line());
102        }
103    }
104
105    pub fn run_frame(&mut self) {
106        let start_frame = self.frame_number();
107        while self.frame_number() == start_frame {
108            self.clock();
109        }
110    }
111
112    pub fn step_cpu_instruction(&mut self) {
113        let start_instruction = self.cpu.instruction_counter();
114        while self.cpu.instruction_counter() == start_instruction {
115            self.clock();
116        }
117    }
118
119    pub fn execute(&mut self, command: CoreCommand) -> CoreResponse {
120        let event = match command {
121            CoreCommand::Reset => {
122                self.reset();
123                CoreEvent::ResetComplete
124            }
125            CoreCommand::SetControllerState { port, state } => {
126                self.set_controller_state(port, state);
127                CoreEvent::ControllerStateUpdated { port }
128            }
129            CoreCommand::RunFrame => {
130                self.run_frame();
131                CoreEvent::FrameReady {
132                    frame_number: self.frame_number(),
133                }
134            }
135            CoreCommand::StepCpuInstruction => {
136                self.step_cpu_instruction();
137                CoreEvent::CpuInstructionComplete {
138                    instruction_counter: self.cpu.instruction_counter(),
139                }
140            }
141        };
142
143        CoreResponse {
144            event,
145            master_clock: self.master_clock,
146        }
147    }
148
149    pub fn master_clock(&self) -> u64 {
150        self.master_clock
151    }
152
153    pub fn frame_number(&self) -> u64 {
154        self.bus.ppu_frame()
155    }
156
157    pub fn frame_pixels(&self) -> &[u8] {
158        self.bus.ppu().frame_pixels()
159    }
160
161    pub fn video_frame(&self) -> VideoFrame<'_> {
162        VideoFrame {
163            width: FRAME_WIDTH,
164            height: FRAME_HEIGHT,
165            pitch: VIDEO_FRAME_PITCH,
166            format: PixelFormat::Indexed8,
167            frame_number: self.frame_number(),
168            pixels: self.frame_pixels(),
169        }
170    }
171
172    pub fn audio_batch(&self) -> AudioBatch<'_> {
173        AudioBatch {
174            channels: 1,
175            sample_rate: self.bus.apu_sample_rate(),
176            samples: self.bus.apu_audio_samples(),
177        }
178    }
179
180    pub fn add_expansion_audio_chip(&mut self, chip: Box<dyn ExpansionAudioChip>) {
181        self.bus.add_expansion_audio_chip(chip);
182    }
183
184    pub fn debug_snapshot(&self) -> DebugSnapshot {
185        let ppu = self.bus.ppu();
186        DebugSnapshot {
187            master_clock: self.master_clock,
188            cpu: self.cpu.debug_snapshot(),
189            ppu: PpuDebugSnapshot {
190                frame: ppu.frame(),
191                scanline: ppu.scanline(),
192                in_vblank: ppu.in_vblank(),
193                nmi_line: ppu.nmi_line(),
194                oam_addr: ppu.oam_addr(),
195            },
196        }
197    }
198
199    pub fn save_state(&self) -> Result<Vec<u8>, SaveStateError> {
200        let mut writer = StateWriter::new();
201        writer.write_u64(self.master_clock);
202        writer.write_u8(self.cpu_ppu_counter);
203        writer.write_u64(self.cpu_schedule_index as u64);
204        self.cpu.save_state(&mut writer);
205        self.bus.save_state(&mut writer)?;
206        Ok(writer.finish())
207    }
208
209    pub fn load_state(&mut self, bytes: &[u8]) -> Result<(), SaveStateError> {
210        let mut reader = StateReader::new(bytes)?;
211        self.master_clock = reader.read_u64()?;
212        self.cpu_ppu_counter = reader.read_u8()?;
213        self.cpu_schedule_index = reader.read_u64()? as usize;
214        self.cpu.load_state(&mut reader)?;
215        self.bus.load_state(&mut reader)?;
216        self.cached_tv_system = self.bus.ppu().tv_system();
217        self.cpu.set_nmi(self.bus.ppu_nmi_line());
218        reader.finish()
219    }
220
221    pub fn clear_audio_samples(&mut self) {
222        self.bus.clear_apu_audio_samples();
223    }
224
225    pub fn set_apu_sample_rate(&mut self, sample_rate: u32) {
226        self.bus.set_apu_sample_rate(sample_rate);
227    }
228
229    pub fn set_apu_debug_mute_mask(&mut self, mask: u8) {
230        self.bus.set_apu_debug_mute_mask(mask);
231    }
232
233    pub fn apu_debug_mute_mask(&self) -> u8 {
234        self.bus.apu_debug_mute_mask()
235    }
236
237    fn reset_cpu_schedule(&mut self) {
238        self.cached_tv_system = self.bus.ppu().tv_system();
239        self.cpu_ppu_counter = 0;
240        self.cpu_schedule_index = 0;
241    }
242}
243
244impl Default for NES {
245    fn default() -> Self {
246        Self::new()
247    }
248}
249
250#[cfg(test)]
251mod tests;