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};
19#[cfg(feature = "debug")]
20pub use api::{Breakpoint, Debugger, DisassembledInstruction, DisassemblyResult, MemorySnapshot};
21pub use apu::ExpansionAudioChip;
22pub use cartridge::{Cartridge, CartridgeError, Mirroring, TVSystem};
23pub use input::{ControllerButton, ControllerState};
24pub use ppu::{FRAME_HEIGHT, FRAME_WIDTH};
25pub use runtime::{
26 ExecutionTarget, FrontendInput, FrontendRuntime, RunMode, RuntimeSnapshot, RuntimeStatus,
27};
28pub use savestate::SaveStateError;
29use savestate::{StateReader, StateWriter};
30
31const PAL_CPU_SCHEDULE: [u8; 5] = [3, 3, 3, 3, 4];
32
33pub struct NES {
34 cpu: cpu::CPU,
35 bus: bus::NESBus,
36 master_clock: u64,
37 cpu_ppu_counter: u8,
38 cpu_schedule_index: usize,
39 cached_tv_system: TVSystem,
40 #[cfg(feature = "debug")]
41 breakpoints: Vec<Breakpoint>,
42 #[cfg(feature = "debug")]
43 cached_mem_breakpoints: Vec<Breakpoint>,
44 #[cfg(feature = "debug")]
45 paused: bool,
46 #[cfg(feature = "debug")]
47 breakpoint_hit: Option<Breakpoint>,
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 #[cfg(feature = "debug")]
60 breakpoints: Vec::new(),
61 #[cfg(feature = "debug")]
62 cached_mem_breakpoints: Vec::new(),
63 #[cfg(feature = "debug")]
64 paused: false,
65 #[cfg(feature = "debug")]
66 breakpoint_hit: None,
67 }
68 }
69
70 pub fn reset(&mut self) {
71 self.bus.reset();
72 self.reset_cpu_schedule();
73 self.cpu.reset(&mut self.bus);
74 self.cpu.set_nmi(self.bus.ppu_nmi_line());
75 }
76
77 pub fn insert_cartridge(&mut self, cartridge: Cartridge) {
78 self.bus.insert_cartridge(cartridge);
79 self.reset_cpu_schedule();
80 self.reset();
81 }
82
83 pub fn load_cartridge_ines(&mut self, rom: &[u8]) -> Result<(), CartridgeError> {
84 self.bus.load_cartridge_ines(rom)?;
85 self.reset_cpu_schedule();
86 Ok(())
87 }
88
89 pub fn set_controller_state(&mut self, port: usize, state: ControllerState) {
90 self.bus.set_controller_state(port, state);
91 }
92
93 pub fn clock(&mut self) {
94 #[cfg(feature = "debug")]
95 if self.paused() {
96 return;
97 }
98
99 self.master_clock += 1;
100 self.bus.tick_ppu();
101
102 #[cfg(feature = "debug")]
103 {
104 if self.check_ppu_breakpoints() {
105 return;
106 }
107 }
108
109 let threshold = match self.cached_tv_system {
110 TVSystem::NTSC | TVSystem::DENDY => (3, 1),
111 TVSystem::PAL => (PAL_CPU_SCHEDULE[self.cpu_schedule_index], 5),
112 };
113 self.cpu_ppu_counter += 1;
114 if self.cpu_ppu_counter >= threshold.0 {
115 self.cpu_ppu_counter = 0;
116 self.cpu_schedule_index = (self.cpu_schedule_index + 1) % threshold.1;
117 self.cpu.set_nmi(self.bus.ppu_nmi_line());
119 self.bus.advance_dma_cpu_phase();
120 self.bus.tick_apu_cpu_cycle();
121
122 #[cfg(feature = "debug")]
123 {
124 self.bus
125 .set_debug_mem_breakpoints(&self.cached_mem_breakpoints);
126 }
127
128 self.cpu.clock(&mut self.bus);
129
130 #[cfg(feature = "debug")]
131 {
132 if let Some(bp) = self.bus.take_mem_breakpoint_hit() {
133 self.breakpoint_hit = Some(bp);
134 return;
135 }
136 }
137
138 self.cpu.irq_set_level(0x01, self.bus.apu_irq_line());
139 self.cpu.irq_set_level(0x02, self.bus.cartridge_irq_line());
140 self.cpu.set_nmi(self.bus.ppu_nmi_line());
141
142 #[cfg(feature = "debug")]
143 {
144 if self.check_breakpoints() {
145 return;
146 }
147 }
148 }
149 }
150
151 pub fn run_frame(&mut self) {
152 let start_frame = self.frame_number();
153 while self.frame_number() == start_frame {
154 self.clock();
155 }
156 }
157
158 pub fn step_cpu_instruction(&mut self) {
159 let start_instruction = self.cpu.instruction_counter();
160 while self.cpu.instruction_counter() == start_instruction {
161 self.clock();
162 }
163 }
164
165 pub fn execute(&mut self, command: CoreCommand) -> CoreResponse {
166 let event = match command {
167 CoreCommand::Reset => {
168 self.reset();
169 CoreEvent::ResetComplete
170 }
171 CoreCommand::SetControllerState { port, state } => {
172 self.set_controller_state(port, state);
173 CoreEvent::ControllerStateUpdated { port }
174 }
175 CoreCommand::RunFrame => {
176 self.run_frame();
177 CoreEvent::FrameReady {
178 frame_number: self.frame_number(),
179 }
180 }
181 CoreCommand::StepCpuInstruction => {
182 self.step_cpu_instruction();
183 CoreEvent::CpuInstructionComplete {
184 instruction_counter: self.cpu.instruction_counter(),
185 }
186 }
187 #[cfg(feature = "debug")]
188 CoreCommand::AddBreakpoint(bp) => {
189 self.add_breakpoint(bp);
190 CoreEvent::None
191 }
192 #[cfg(feature = "debug")]
193 CoreCommand::RemoveBreakpoint(bp) => {
194 self.remove_breakpoint(&bp);
195 CoreEvent::None
196 }
197 #[cfg(feature = "debug")]
198 CoreCommand::SetPaused(paused) => {
199 self.set_paused(paused);
200 CoreEvent::None
201 }
202 };
203
204 CoreResponse {
205 event,
206 master_clock: self.master_clock,
207 }
208 }
209
210 pub fn master_clock(&self) -> u64 {
211 self.master_clock
212 }
213
214 pub fn frame_number(&self) -> u64 {
215 self.bus.ppu_frame()
216 }
217
218 pub fn frame_pixels(&self) -> &[u8] {
219 self.bus.ppu().frame_pixels()
220 }
221
222 pub fn video_frame(&self) -> VideoFrame<'_> {
223 VideoFrame {
224 width: FRAME_WIDTH,
225 height: FRAME_HEIGHT,
226 pitch: VIDEO_FRAME_PITCH,
227 format: PixelFormat::Indexed8,
228 frame_number: self.frame_number(),
229 pixels: self.frame_pixels(),
230 }
231 }
232
233 pub fn audio_batch(&self) -> AudioBatch<'_> {
234 AudioBatch {
235 channels: 1,
236 sample_rate: self.bus.apu_sample_rate(),
237 samples: self.bus.apu_audio_samples(),
238 }
239 }
240
241 pub fn apu_audio_samples(&self) -> &[f32] {
242 self.bus.apu_audio_samples()
243 }
244
245 pub fn apu_sample_rate(&self) -> u32 {
246 self.bus.apu_sample_rate()
247 }
248
249 pub fn clear_apu_audio_samples(&mut self) {
250 self.bus.clear_apu_audio_samples();
251 }
252
253 pub fn add_expansion_audio_chip(&mut self, chip: Box<dyn ExpansionAudioChip>) {
254 self.bus.add_expansion_audio_chip(chip);
255 }
256
257 pub fn debug_snapshot(&self) -> DebugSnapshot {
258 let ppu = self.bus.ppu();
259 DebugSnapshot {
260 master_clock: self.master_clock,
261 cpu: self.cpu.debug_snapshot(),
262 #[cfg(feature = "debug")]
263 ppu: PpuDebugSnapshot {
264 frame: ppu.frame(),
265 scanline: ppu.scanline(),
266 in_vblank: ppu.in_vblank(),
267 nmi_line: ppu.nmi_line(),
268 oam_addr: ppu.oam_addr(),
269 cycles: ppu.cycles(),
270 ctrl: ppu.debug_ctrl(),
271 mask: ppu.debug_mask(),
272 status: ppu.debug_status(),
273 fine_x: ppu.debug_fine_x(),
274 vram_addr: ppu.debug_vram_addr(),
275 temp_vram_addr: ppu.debug_temp_vram_addr(),
276 write_latch: ppu.debug_write_latch(),
277 bg_on: ppu.bg_on(),
278 sprites_on: ppu.sprites_on(),
279 rendering_on: ppu.rendering_on(),
280 odd_frame: ppu.debug_odd_frame(),
281 },
282 #[cfg(not(feature = "debug"))]
283 ppu: PpuDebugSnapshot {
284 frame: ppu.frame(),
285 scanline: ppu.scanline(),
286 in_vblank: ppu.in_vblank(),
287 nmi_line: ppu.nmi_line(),
288 oam_addr: ppu.oam_addr(),
289 },
290 }
291 }
292
293 #[cfg(feature = "debug")]
294 pub fn debug_memory_snapshot(&self) -> MemorySnapshot<'_> {
295 self.bus.debug_memory_snapshot()
296 }
297
298 pub fn mirroring(&self) -> Mirroring {
299 self.bus.mirroring()
300 }
301
302 #[cfg(feature = "debug")]
303 pub fn debug_read_chr(&mut self) -> [u8; 0x2000] {
304 self.bus.debug_read_chr()
305 }
306
307 #[cfg(feature = "debug")]
308 pub fn debug_disassemble(&mut self, rows: usize) -> DisassemblyResult {
309 let pc = self.cpu.pc();
310 cpu::disassemble_range(&mut self.bus, pc, rows, rows)
311 }
312
313 pub fn save_state(&self) -> Result<Vec<u8>, SaveStateError> {
314 let mut writer = StateWriter::new();
315 writer.write_u64(self.master_clock);
316 writer.write_u8(self.cpu_ppu_counter);
317 writer.write_u64(self.cpu_schedule_index as u64);
318 self.cpu.save_state(&mut writer);
319 self.bus.save_state(&mut writer)?;
320 Ok(writer.finish())
321 }
322
323 pub fn load_state(&mut self, bytes: &[u8]) -> Result<(), SaveStateError> {
324 let mut reader = StateReader::new(bytes)?;
325 self.master_clock = reader.read_u64()?;
326 self.cpu_ppu_counter = reader.read_u8()?;
327 self.cpu_schedule_index = reader.read_u64()? as usize;
328 self.cpu.load_state(&mut reader)?;
329 self.bus.load_state(&mut reader)?;
330 self.cached_tv_system = self.bus.ppu().tv_system();
331 self.cpu.set_nmi(self.bus.ppu_nmi_line());
332 reader.finish()
333 }
334
335 pub fn clear_audio_samples(&mut self) {
336 self.bus.clear_apu_audio_samples();
337 }
338
339 pub fn set_apu_sample_rate(&mut self, sample_rate: u32) {
340 self.bus.set_apu_sample_rate(sample_rate);
341 }
342
343 pub fn set_apu_debug_mute_mask(&mut self, mask: u8) {
344 self.bus.set_apu_debug_mute_mask(mask);
345 }
346
347 pub fn apu_debug_mute_mask(&self) -> u8 {
348 self.bus.apu_debug_mute_mask()
349 }
350
351 #[cfg(feature = "debug")]
352 fn update_mem_breakpoint_cache(&mut self) {
353 self.cached_mem_breakpoints = self
354 .breakpoints
355 .iter()
356 .copied()
357 .filter(|bp| matches!(bp, Breakpoint::MemoryRead(_) | Breakpoint::MemoryWrite(_)))
358 .collect();
359 }
360
361 #[cfg(feature = "debug")]
362 pub fn add_breakpoint(&mut self, bp: Breakpoint) {
363 if !self.breakpoints.contains(&bp) {
364 self.breakpoints.push(bp);
365 self.update_mem_breakpoint_cache();
366 }
367 }
368
369 #[cfg(feature = "debug")]
370 pub fn remove_breakpoint(&mut self, bp: &Breakpoint) {
371 self.breakpoints.retain(|b| b != bp);
372 self.update_mem_breakpoint_cache();
373 }
374
375 #[cfg(feature = "debug")]
376 pub fn clear_breakpoints(&mut self) {
377 self.breakpoints.clear();
378 self.cached_mem_breakpoints.clear();
379 }
380
381 #[cfg(feature = "debug")]
382 pub fn breakpoints(&self) -> &[Breakpoint] {
383 &self.breakpoints
384 }
385
386 #[cfg(feature = "debug")]
387 pub fn set_paused(&mut self, paused: bool) {
388 self.paused = paused;
389 self.breakpoint_hit = None;
390 }
391
392 #[cfg(feature = "debug")]
393 pub fn paused(&self) -> bool {
394 self.paused || self.breakpoint_hit.is_some()
395 }
396
397 #[cfg(feature = "debug")]
398 pub fn breakpoint_hit(&self) -> Option<Breakpoint> {
399 self.breakpoint_hit
400 }
401
402 #[cfg(feature = "debug")]
403 fn check_breakpoints(&mut self) -> bool {
404 if self.breakpoints.is_empty() {
405 return false;
406 }
407
408 let pc = self.cpu.pc();
409
410 for &bp in &self.breakpoints {
411 match bp {
412 Breakpoint::Address(addr) if pc == addr => {
413 self.breakpoint_hit = Some(bp);
414 return true;
415 }
416 _ => {}
417 }
418 }
419 false
420 }
421
422 #[cfg(feature = "debug")]
423 fn check_ppu_breakpoints(&mut self) -> bool {
424 if self.breakpoints.is_empty() {
425 return false;
426 }
427
428 let ppu = self.bus.ppu();
429 let scanline = ppu.scanline();
430 let in_vblank = ppu.in_vblank();
431
432 for &bp in &self.breakpoints {
433 match bp {
434 Breakpoint::PpuScanline(sl) if scanline == sl => {
435 self.breakpoint_hit = Some(bp);
436 return true;
437 }
438 Breakpoint::Vblank if in_vblank => {
439 self.breakpoint_hit = Some(bp);
440 return true;
441 }
442 _ => {}
443 }
444 }
445 false
446 }
447
448 fn reset_cpu_schedule(&mut self) {
449 self.cached_tv_system = self.bus.ppu().tv_system();
450 self.cpu_ppu_counter = 0;
451 self.cpu_schedule_index = 0;
452 }
453}
454
455impl Default for NES {
456 fn default() -> Self {
457 Self::new()
458 }
459}
460
461#[cfg(test)]
462mod tests;