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)]
106pub 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}