ruboy_lib/
lib.rs

1use std::fmt::Display;
2use std::time::Instant;
3
4use cpu::Cpu;
5use cpu::CpuErr;
6use input::apply_input_to;
7use memcontroller::MemController;
8
9use memcontroller::MemControllerInitErr;
10use memcontroller::WriteError;
11use ppu::PpuErr;
12use ppu::{Ppu, FRAME_CYCLES};
13use thiserror::Error;
14
15mod boot;
16mod cpu;
17mod extern_traits;
18mod input;
19pub mod isa;
20mod memcontroller;
21mod ppu;
22pub mod rom;
23
24pub use extern_traits::*;
25
26pub const CLOCK_SPEED_HZ: usize = 1 << 22;
27pub const CLOCK_SPEED_HZ_F64: f64 = CLOCK_SPEED_HZ as f64;
28pub const DESIRED_FRAMERATE: f64 = CLOCK_SPEED_HZ_F64 / (FRAME_CYCLES as f64);
29
30pub struct Ruboy<A, R, V, I>
31where
32    A: GBAllocator,
33    R: RomReader,
34    V: GBGraphicsDrawer,
35    I: InputHandler,
36{
37    cycle_accumulator: f64,
38    cpu: Cpu,
39    ppu: Ppu<V>,
40    mem: MemController<A, R>,
41    input: I,
42}
43
44#[derive(Debug, Clone, Copy)]
45enum Frequency {
46    None(f64),
47    Kilo(f64),
48    Mega(f64),
49    Giga(f64),
50}
51
52impl Frequency {
53    pub fn new(val: f64) -> Self {
54        Self::None(val).upcast()
55    }
56
57    pub fn val_raw(self) -> f64 {
58        match self {
59            Frequency::None(x) => x,
60            Frequency::Kilo(x) => x * 1000.0,
61            Frequency::Mega(x) => x * (1000.0 * 1000.0),
62            Frequency::Giga(x) => x * (1000.0 * 1000.0 * 1000.0),
63        }
64    }
65
66    pub fn val_unit(self) -> f64 {
67        match self {
68            Frequency::None(x) => x,
69            Frequency::Kilo(x) => x,
70            Frequency::Mega(x) => x,
71            Frequency::Giga(x) => x,
72        }
73    }
74    pub fn upcast(self) -> Self {
75        if self.val_unit() > 1000.0 {
76            match self {
77                Frequency::None(x) => Frequency::Kilo(x / 1000.0).upcast(),
78                Frequency::Kilo(x) => Frequency::Mega(x / 1000.0).upcast(),
79                Frequency::Mega(x) => Frequency::Giga(x / 1000.0).upcast(),
80                Frequency::Giga(_) => self,
81            }
82        } else {
83            self
84        }
85    }
86}
87
88impl Display for Frequency {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        match *self {
91            Frequency::None(x) => write!(f, "{:.6}Hz", x),
92            Frequency::Kilo(x) => write!(f, "{:.6}KHz", x),
93            Frequency::Mega(x) => write!(f, "{:.6}MHz", x),
94            Frequency::Giga(x) => write!(f, "{:.6}GHz", x),
95        }
96    }
97}
98
99#[derive(Debug, Error)]
100pub enum RuboyStartErr<R: RomReader> {
101    #[error("Could not initialize memory controller: {0}")]
102    MemController(#[from] MemControllerInitErr<R>),
103}
104
105#[derive(Debug, Error)]
106// #[derive(Debug)]
107pub enum RuboyErr<V: GBGraphicsDrawer> {
108    #[error("Error during CPU cycle")]
109    Cpu(#[from] CpuErr),
110
111    #[error("Error during PPU cycle")]
112    Ppu(#[from] PpuErr<V>),
113
114    #[error("Error during DMA cycle")]
115    Dma(#[source] WriteError),
116}
117
118impl<A: GBAllocator, R: RomReader, V: GBGraphicsDrawer, I: InputHandler> Ruboy<A, R, V, I> {
119    pub fn new(rom: R, output: V, input: I) -> Result<Self, RuboyStartErr<R>> {
120        Ok(Self {
121            cycle_accumulator: 0.0,
122            cpu: Cpu::new(),
123            ppu: Ppu::new(output),
124            mem: MemController::new(rom)?,
125            input,
126        })
127    }
128
129    pub fn step(&mut self, dt: f64) -> Result<usize, RuboyErr<V>> {
130        log::debug!("Stepping emulator {} seconds", dt);
131
132        let cycles_dt = dt * CLOCK_SPEED_HZ_F64;
133        let (mut cycles_to_run, accumulated) = split_f64(cycles_dt);
134
135        self.cycle_accumulator += accumulated;
136        let (extra_cycles, new_accumulator) = split_f64(self.cycle_accumulator);
137
138        cycles_to_run += extra_cycles;
139        self.cycle_accumulator = new_accumulator;
140
141        debug_assert!(cycles_to_run >= 0);
142
143        log::trace!("Running {} cycles", cycles_to_run as usize);
144
145        for _ in 0..(cycles_to_run as usize) {
146            let (new_joypad_reg_value, can_raise_joypad_interrupt) =
147                apply_input_to(self.mem.io_registers.joypad, self.input.get_new_inputs());
148
149            self.mem.io_registers.joypad = new_joypad_reg_value;
150            if can_raise_joypad_interrupt {
151                self.mem.io_registers.interrupts_requested.set_joypad(true);
152            }
153
154            self.cpu.run_cycle(&mut self.mem)?;
155            self.ppu.run_cycle(&mut self.mem)?;
156            self.mem.dma_cycle().map_err(|e| RuboyErr::Dma(e))?;
157        }
158
159        Ok(cycles_to_run as usize)
160    }
161}
162
163fn split_f64(f: f64) -> (i64, f64) {
164    let truncated = f.trunc();
165
166    (truncated as i64, f - truncated)
167}