Skip to main content

padme_core/
system.rs

1use core::ops::Deref;
2use core::time::Duration;
3
4use crate::{Button, Error, Rom, Screen, AudioSpeaker, SerialOutput};
5use crate::bus::Bus;
6use crate::cpu::{Cpu, CLOCK_SPEED};
7
8pub const DEFAULT_FRAME_RATE: u32 = 60;
9
10pub struct System<T: Deref<Target=[u8]>,
11                  S: Screen,
12                  SO: SerialOutput,
13                  AS: AudioSpeaker> {
14    /// Address bus
15    bus: Bus<T>,
16    /// To execute instructions
17    cpu: Cpu,
18    /// A screen to give to the PPU
19    screen: S,
20    /// A serial output to give to the serial controller
21    serial_output: SO,
22    /// An audio speaker interface
23    speaker: AS,
24    /// Keep the number of cycles before a frame is refreshed
25    cycles_per_frame: u32,
26}
27
28impl<T: Deref<Target=[u8]>,
29     S: Screen,
30     SO: SerialOutput,
31     AS: AudioSpeaker> System<T, S, SO, AS> {
32    pub fn new(rom: Rom<T>, screen: S, serial_output: SO, speaker: AS) -> Self {
33        let bus = Bus::new(rom);
34        let cpu = Cpu::new();
35
36        System {
37            bus,
38            cpu,
39            screen,
40            serial_output,
41            speaker,
42            cycles_per_frame: CLOCK_SPEED / DEFAULT_FRAME_RATE,
43        }
44    }
45
46    pub fn reset(&mut self) {
47        self.bus.ppu.reset();
48        self.bus.timer.reset();
49        self.bus.serial.reset();
50        self.bus.joypad.reset();
51        self.bus.it.reset();
52        self.cpu.reset();
53    }
54
55    /// Replace cartridge with a new buffer
56    pub fn load_bin(&mut self, bytes: T) -> Result<(), Error> {
57        let rom = Rom::load(bytes)?;
58
59        self.reset();
60        self.bus.set_rom(rom);
61        Ok(())
62    }
63
64    /// Reload a new rom
65    pub fn load_rom(&mut self, rom: Rom<T>) {
66        self.bus.set_rom(rom);
67        self.reset();
68    }
69
70    /// Single step to execute cpu, ppu, timer, serial & dma
71    pub fn step(&mut self) -> u8 {
72        let ticks = self.cpu.step(&mut self.bus);
73
74        for _ in 0..ticks {
75            self.bus.apu.step(&mut self.speaker);
76            self.bus.ppu.step(&mut self.screen, &mut self.bus.it);
77            self.bus.timer.step(&mut self.bus.it);
78        }
79
80        self.bus.serial.step(&mut self.serial_output, &mut self.bus.it);
81
82        self.bus.dma_tick();
83
84        ticks
85    }
86
87    /// Retrieve the rom in readonly
88    pub fn rom(&self) -> &Rom<T> {
89        &self.bus.rom
90    }
91
92    /// Retrieve the screen
93    pub fn screen(&mut self) -> &mut S {
94        &mut self.screen
95    }
96
97    /// Retrieve the serial output
98    pub fn serial(&mut self) -> &mut SO {
99        &mut self.serial_output
100    }
101
102    /// Retrieve the speaker
103    pub fn speaker(&mut self) -> &mut AS {
104        &mut self.speaker
105    }
106
107    /// Forward a button press to the joypad controller
108    /// ```
109    /// # use padme_core::*;
110    /// # use padme_core::default::*;
111    /// #
112    /// # let mut bin = [0u8; 32 * 1024];
113    /// # let mut rom = Rom::load(&mut bin[..]).unwrap();
114    /// let mut emu = System::new(rom, NoScreen, NoSerial, NoSpeaker);
115    /// emu.set_button(Button::A, true);
116    /// emu.set_button(Button::Up, true);
117    /// ```
118    pub fn set_button(&mut self, button: Button, is_pressed: bool) {
119        self.bus.joypad.set_button(button, is_pressed, &mut self.bus.it);
120    }
121
122    /// Sets the FPS (default = 60)
123    pub fn set_frame_rate(&mut self, fps: u32) {
124        if fps > 0 && fps < CLOCK_SPEED {
125            self.cycles_per_frame = CLOCK_SPEED / fps;
126        }
127    }
128
129    /// Execute enough steps to retrieve 1 frame
130    /// ```
131    /// # use padme_core::*;
132    /// # use padme_core::default::*;
133    /// # use std::time::Instant;
134    /// # use std::thread::sleep;
135    /// #
136    /// # let mut bin = [0u8; 32 * 1024];
137    /// # let mut rom = Rom::load(&mut bin[..]).unwrap();
138    /// let mut emu = System::new(rom, NoScreen, NoSerial, NoSpeaker);
139    /// // loop {
140    ///     let t0 = Instant::now();
141    ///     emu.update_frame();
142    ///     let frame_time = t0.elapsed();
143    ///     let min_frame_time = emu.min_frame_time();
144    ///     if frame_time < min_frame_time {
145    ///         sleep(min_frame_time - frame_time);
146    ///     }
147    /// // }
148    /// ```
149    pub fn update_frame(&mut self) -> u32 {
150        let mut cycles = 0u32;
151        while cycles < self.cycles_per_frame {
152            cycles += self.step() as u32;
153        }
154        self.screen.update();
155        cycles
156    }
157
158    /// Returns the minimum amount of time to wait between each frame
159    /// Mostly depend on the FPS
160    pub fn min_frame_time(&self) -> Duration {
161        Duration::from_millis(1000 / (CLOCK_SPEED / self.cycles_per_frame) as u64)
162    }
163}