1mod apu;
2mod cartridge;
3pub mod cpu;
4mod joypad;
5pub mod memory;
6mod ppu;
7mod printer;
8mod save_error;
9mod serial;
10mod timer;
11
12#[cfg(test)]
13mod tests;
14
15use std::cell::RefCell;
16use std::fs::File;
17use std::io::{Cursor, Read, Seek, SeekFrom, Write};
18use std::path::{Path, PathBuf};
19use std::rc::Rc;
20
21use save_state::Savable;
22
23use cartridge::Cartridge;
24use cpu::Cpu;
25use memory::Bus;
26
27pub use apu::AudioBuffers;
28pub use cartridge::CartridgeError;
29pub use joypad::JoypadButton;
30pub use printer::Printer;
31pub use save_error::SaveError;
32pub use serial::SerialDevice;
33
34pub const SAVE_STATE_VERSION: usize = 2;
40const SAVE_STATE_MAGIC: &[u8; 4] = b"MST\xee";
41const SAVE_STATE_ZSTD_COMPRESSION_LEVEL: i32 = 0; #[derive(Debug, Default, Clone, Copy, Savable)]
45pub struct GameBoyConfig {
46 pub is_dmg: bool,
48}
49
50impl GameBoyConfig {
51 pub fn boot_rom_len(&self) -> usize {
52 if self.is_dmg {
53 0x100
54 } else {
55 0x900
56 }
57 }
58}
59
60pub struct GameBoyBuilder {
62 config: GameBoyConfig,
63 rom_file: PathBuf,
64 boot_rom_file: Option<PathBuf>,
65 sram_file: Option<PathBuf>,
66 save_on_shutdown: bool,
67}
68
69impl GameBoyBuilder {
70 pub fn config(mut self, config: GameBoyConfig) -> Self {
72 self.config = config;
73 self
74 }
75
76 pub fn boot_rom_file<P: AsRef<Path>>(mut self, boot_rom_file: P) -> Self {
78 self.boot_rom_file = Some(boot_rom_file.as_ref().to_path_buf());
79 self
80 }
81
82 pub fn sram_file<P: AsRef<Path>>(mut self, save_file: P) -> Self {
86 self.sram_file = Some(save_file.as_ref().to_path_buf());
87 self
88 }
89
90 pub fn save_on_shutdown(mut self, save_on_shutdown: bool) -> Self {
92 self.save_on_shutdown = save_on_shutdown;
93 self
94 }
95
96 pub fn build(self) -> Result<GameBoy, CartridgeError> {
98 GameBoy::build(self)
99 }
100}
101
102pub struct GameBoy {
106 cpu: Cpu,
107 bus: Bus,
108}
109
110impl GameBoy {
111 pub fn builder<RomP: AsRef<Path>>(rom_file: RomP) -> GameBoyBuilder {
113 GameBoyBuilder {
114 config: GameBoyConfig::default(),
115 rom_file: rom_file.as_ref().to_path_buf(),
116 boot_rom_file: None,
117 sram_file: None,
118 save_on_shutdown: true,
119 }
120 }
121
122 fn build(builder: GameBoyBuilder) -> Result<Self, CartridgeError> {
123 let file_path = builder.rom_file;
124 let sram_file_path = builder.sram_file;
125 let boot_rom_file_path = builder.boot_rom_file;
126 let config = builder.config;
127 let save_on_shutdown = builder.save_on_shutdown;
128
129 let cartridge = Cartridge::from_file(file_path, sram_file_path, save_on_shutdown)?;
130
131 let (bus, cpu) = if let Some(boot_rom_file) = boot_rom_file_path {
132 let mut boot_rom_file = File::open(boot_rom_file)?;
133 let mut data = vec![0; config.boot_rom_len()];
134
135 assert_eq!(
137 boot_rom_file.metadata()?.len(),
138 data.len() as u64,
139 "boot_rom file size is not correct"
140 );
141
142 boot_rom_file.read_exact(&mut data)?;
143
144 (
145 Bus::new_with_boot_rom(cartridge, data, config),
146 Cpu::new(config),
147 )
148 } else {
149 let is_cartridge_color = cartridge.is_cartridge_color();
150 (
151 Bus::new_without_boot_rom(cartridge, config),
152 Cpu::new_without_boot_rom(config, is_cartridge_color),
153 )
154 };
155
156 Ok(Self { bus, cpu })
157 }
158
159 pub fn clock_for_frame(&mut self) {
164 const PPU_CYCLES_PER_FRAME: u32 = 456 * 154;
165 let mut cycles = 0u32;
166 while cycles < PPU_CYCLES_PER_FRAME {
167 self.cpu.next_instruction(&mut self.bus).unwrap();
168 cycles += self.bus.elapsed_ppu_cycles();
169 }
170 }
171
172 pub fn game_title(&self) -> &str {
174 self.bus.cartridge().game_title()
175 }
176
177 pub fn file_path(&self) -> &Path {
179 self.bus.cartridge().file_path()
180 }
181
182 pub fn screen_buffer(&self) -> &[u8] {
186 self.bus.screen_buffer()
187 }
188
189 pub fn audio_buffers(&mut self) -> AudioBuffers<'_> {
193 self.bus.audio_buffers()
194 }
195
196 pub fn press_joypad(&mut self, button: JoypadButton) {
198 self.bus.press_joypad(button);
199 }
200
201 pub fn release_joypad(&mut self, button: JoypadButton) {
203 self.bus.release_joypad(button);
204 }
205
206 pub fn connect_device(&mut self, device: Rc<RefCell<dyn SerialDevice>>) {
212 self.bus.connect_device(device);
213 }
214
215 pub fn disconnect_device(&mut self) {
217 self.bus.disconnect_device();
218 }
219
220 pub fn save_state<W: Write>(&self, mut writer: W) -> Result<(), SaveError> {
222 SAVE_STATE_MAGIC.save(&mut writer)?;
223 SAVE_STATE_VERSION.save(&mut writer)?;
224 let cartridge_hash: &[u8; 32] = self.bus.cartridge().hash();
225 cartridge_hash.save(&mut writer)?;
226
227 let mut writer = zstd::Encoder::new(&mut writer, SAVE_STATE_ZSTD_COMPRESSION_LEVEL)?;
228
229 self.cpu.save(&mut writer)?;
230 self.bus.save(&mut writer)?;
231
232 let _writer = writer.finish()?;
233
234 Ok(())
235 }
236
237 pub fn load_state<R: Read + Seek>(&mut self, mut reader: R) -> Result<(), SaveError> {
241 let mut recovery_save_state = Vec::new();
243 self.cpu
244 .save(&mut recovery_save_state)
245 .expect("recovery save cpu");
246 self.bus
247 .save(&mut recovery_save_state)
248 .expect("recovery save bus");
249
250 let mut load_routine = || {
251 let mut magic = [0u8; 4];
252 let mut version = 0usize;
253 let mut hash = [0u8; 32];
254
255 magic.load(&mut reader)?;
256 if &magic != SAVE_STATE_MAGIC {
257 return Err(SaveError::InvalidSaveStateHeader);
258 }
259
260 version.load(&mut reader)?;
263
264 hash.load(&mut reader)?;
265 if &hash != self.bus.cartridge().hash() {
266 return Err(SaveError::InvalidCartridgeHash);
267 }
268
269 {
270 let mut second_stage_reader: Box<dyn Read>;
274
275 if version == 1 && SAVE_STATE_VERSION == 2 {
276 second_stage_reader = Box::new(&mut reader);
278 } else if version != SAVE_STATE_VERSION {
279 return Err(SaveError::UnmatchedSaveErrorVersion(version));
280 } else {
281 second_stage_reader = Box::new(zstd::Decoder::new(&mut reader)?);
282 }
283
284 self.cpu.load(&mut second_stage_reader)?;
285 self.bus.load(&mut second_stage_reader)?;
286 }
287
288 let stream_current_pos = reader.stream_position()?;
290 reader.seek(SeekFrom::End(0))?;
291 let stream_last_pos = reader.stream_position()?;
292
293 let (remaining_data_len, overflow) =
294 stream_last_pos.overflowing_sub(stream_current_pos);
295 assert!(!overflow);
296
297 if remaining_data_len > 0 {
298 reader.seek(SeekFrom::Start(stream_current_pos))?;
300
301 Err(SaveError::SaveStateError(save_state::Error::TrailingData(
302 remaining_data_len,
303 )))
304 } else {
305 Ok(())
306 }
307 };
308
309 if let Err(err) = load_routine() {
310 let mut cursor = Cursor::new(&recovery_save_state);
311
312 self.cpu.load(&mut cursor).expect("recovery load cpu");
313 self.bus.load(&mut cursor).expect("recovery load bus");
314
315 Err(err)
316 } else {
317 Ok(())
318 }
319 }
320}