1mod dma;
2pub mod interrupts;
3
4use save_state::Savable;
5
6use std::cell::RefCell;
7use std::rc::Rc;
8
9pub use interrupts::{InterruptManager, InterruptType};
10
11use crate::apu::{Apu, AudioBuffers};
12use crate::cartridge::Cartridge;
13use crate::cpu::CpuBusProvider;
14use crate::joypad::{Joypad, JoypadButton};
15use crate::ppu::Ppu;
16use crate::serial::{Serial, SerialDevice};
17use crate::timer::Timer;
18use crate::GameBoyConfig;
19use dma::{BusType, Hdma, OamDma};
20use interrupts::Interrupts;
21
22#[derive(Default, Savable)]
23struct BootRom {
24 enabled: bool,
25 data: Vec<u8>,
26}
27
28#[derive(Clone, Copy, PartialEq, Debug, Savable)]
29enum Speed {
30 Normal,
31 Double,
32}
33
34impl Default for Speed {
35 fn default() -> Self {
36 Self::Normal
37 }
38}
39
40#[derive(Default, Savable)]
41struct SpeedController {
42 preparing_switch: bool,
43 current_speed: Speed,
44}
45
46impl SpeedController {
47 fn read_key1(&self) -> u8 {
48 0x7E | ((self.current_speed as u8) << 7) | self.preparing_switch as u8
49 }
50
51 fn write_key1(&mut self, data: u8) {
52 self.preparing_switch = data & 1 != 0;
53 }
54
55 fn preparing_switch(&self) -> bool {
56 self.preparing_switch
57 }
58
59 fn current_speed(&self) -> Speed {
60 self.current_speed
61 }
62
63 fn commit_speed_switch(&mut self) {
64 assert!(self.preparing_switch);
65 self.current_speed = match self.current_speed {
66 Speed::Normal => Speed::Double,
67 Speed::Double => Speed::Normal,
68 };
69 self.preparing_switch = false;
70 }
71}
72
73#[derive(Savable)]
74struct Wram {
75 data: [u8; 0x8000],
76 bank: u8,
77}
78
79impl Default for Wram {
80 fn default() -> Self {
81 Self {
82 data: [0; 0x8000],
83 bank: 1,
84 }
85 }
86}
87
88impl Wram {
89 fn read_wram0(&self, addr: u16) -> u8 {
90 self.data[addr as usize & 0xFFF]
91 }
92
93 fn read_wramx(&self, addr: u16) -> u8 {
94 self.data[(0x1000 * self.bank as usize) + (addr as usize & 0xFFF)]
95 }
96
97 fn write_wram0(&mut self, addr: u16, data: u8) {
98 self.data[addr as usize & 0xFFF] = data;
99 }
100
101 fn write_wramx(&mut self, addr: u16, data: u8) {
102 self.data[(0x1000 * self.bank as usize) + (addr as usize & 0xFFF)] = data;
103 }
104
105 fn set_wram_bank(&mut self, data: u8) {
106 self.bank = data & 7;
107 if self.bank == 0 {
109 self.bank = 1;
110 }
111 }
112
113 fn get_wram_bank(&self) -> u8 {
114 0xF8 | self.bank
115 }
116}
117
118#[derive(Savable)]
119struct Lock {
120 during_boot: bool,
121 is_dmg_mode: bool,
122 written_to: bool,
123}
124
125impl Default for Lock {
126 fn default() -> Self {
127 Self {
128 during_boot: true,
129 is_dmg_mode: false,
130 written_to: false,
131 }
132 }
133}
134
135impl Lock {
136 fn write(&mut self, data: u8) {
137 if !self.written_to {
140 self.written_to = true;
141 self.is_dmg_mode = data & 0x4 != 0;
142 }
143 }
144
145 fn finish_boot(&mut self) {
146 self.during_boot = false;
147 }
148
149 fn is_cgb_mode(&self) -> bool {
152 !self.is_dmg_mode || self.during_boot
153 }
154}
155
156#[derive(Savable)]
157struct UnknownRegister {
158 data: u8,
159 mask: u8,
160}
161
162impl UnknownRegister {
163 fn new(mask: u8) -> Self {
164 Self { data: 0, mask }
165 }
166
167 fn read(&self) -> u8 {
168 (self.data & self.mask) | !self.mask
169 }
170
171 fn write(&mut self, data: u8) {
172 self.data = data & self.mask
173 }
174}
175
176#[derive(Savable)]
178struct UnknownRegisters {
179 registers: [UnknownRegister; 4],
180}
181
182impl UnknownRegisters {
183 pub fn new(masks: [u8; 4]) -> Self {
184 Self {
185 registers: [
186 UnknownRegister::new(masks[0]),
187 UnknownRegister::new(masks[1]),
188 UnknownRegister::new(masks[2]),
189 UnknownRegister::new(masks[3]),
190 ],
191 }
192 }
193}
194
195impl std::ops::Index<usize> for UnknownRegisters {
196 type Output = UnknownRegister;
197
198 fn index(&self, index: usize) -> &Self::Output {
199 &self.registers[index]
200 }
201}
202
203impl std::ops::IndexMut<usize> for UnknownRegisters {
204 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
205 &mut self.registers[index]
206 }
207}
208
209#[derive(Savable)]
210pub struct Bus {
211 cartridge: Cartridge,
212 ppu: Ppu,
213 wram: Wram,
214 interrupts: Interrupts,
215 timer: Timer,
216 joypad: Joypad,
217 serial: Serial,
218 oam_dma: OamDma,
219 hdma: Hdma,
220 apu: Apu,
221 hram: [u8; 127],
222 boot_rom: BootRom,
223 speed_controller: SpeedController,
224 lock: Lock,
225 unknown_registers: UnknownRegisters,
226
227 #[savable(skip)]
228 serial_device: Option<Rc<RefCell<dyn SerialDevice>>>,
229
230 stopped: bool,
231
232 elapsed_ppu_cycles: u32,
235
236 config: GameBoyConfig,
237}
238
239impl Bus {
240 pub fn new_without_boot_rom(cartridge: Cartridge, config: GameBoyConfig) -> Self {
241 let cgb_mode = cartridge.is_cartridge_color();
242 let mut lock = Lock::default();
243
244 if !cgb_mode || config.is_dmg {
245 lock.write(4);
246 } else {
247 lock.write(0x80);
249 }
250
251 lock.finish_boot();
252
253 Self {
254 cartridge,
255 ppu: Ppu::new_skip_boot_rom(cgb_mode, config),
256 wram: Wram::default(),
257 interrupts: Interrupts::default(),
258 timer: Timer::new_skip_boot_rom(config),
259 joypad: Joypad::default(),
260 serial: Serial::new_skip_boot_rom(config),
261 oam_dma: OamDma::default(),
262 hdma: Hdma::default(),
263 apu: Apu::new_skip_boot_rom(config),
264 hram: [0; 127],
265 boot_rom: BootRom::default(),
266 speed_controller: SpeedController::default(),
267 lock,
268 unknown_registers: UnknownRegisters::new([0xFF, 0xFF, 0xFF, 0x70]),
269 serial_device: None,
270 stopped: false,
271
272 elapsed_ppu_cycles: 0,
273
274 config,
275 }
276 }
277
278 pub fn new_with_boot_rom(
279 cartridge: Cartridge,
280 boot_rom_data: Vec<u8>,
281 config: GameBoyConfig,
282 ) -> Self {
283 let mut s = Self::new_without_boot_rom(cartridge, config);
284 s.timer = Timer::default();
285 s.ppu = Ppu::new(config);
286 s.apu = Apu::new(config);
287 s.serial = Serial::new(config);
288 s.lock = Lock::default();
289
290 if config.is_dmg {
291 s.lock.write(4);
292 s.lock.finish_boot();
293 }
294
295 assert_eq!(
298 boot_rom_data.len(),
299 config.boot_rom_len(),
300 "Bootrom length does not match"
301 );
302
303 s.boot_rom.data = boot_rom_data;
304 s.boot_rom.enabled = true;
305 s
306 }
307
308 pub fn cartridge(&self) -> &Cartridge {
309 &self.cartridge
310 }
311
312 pub fn screen_buffer(&self) -> &[u8] {
313 self.ppu.screen_buffer()
314 }
315
316 #[cfg(test)]
317 pub(crate) fn raw_screen_buffer(&self) -> &[u8] {
318 self.ppu.raw_screen_buffer()
319 }
320
321 pub fn audio_buffers(&mut self) -> AudioBuffers<'_> {
322 self.apu.get_buffers()
323 }
324
325 pub fn press_joypad(&mut self, button: JoypadButton) {
326 self.joypad.press_joypad(button);
327 }
328
329 pub fn release_joypad(&mut self, button: JoypadButton) {
330 self.joypad.release_joypad(button);
331 }
332
333 pub fn connect_device(&mut self, device: Rc<RefCell<dyn SerialDevice>>) {
334 self.serial_device = Some(device);
335 }
336
337 pub fn disconnect_device(&mut self) {
338 self.serial_device = None;
339 }
340
341 pub fn elapsed_ppu_cycles(&mut self) -> u32 {
342 std::mem::replace(&mut self.elapsed_ppu_cycles, 0)
343 }
344}
345
346impl Bus {
347 fn on_cpu_machine_cycle(&mut self) {
348 let double_speed = self.speed_controller.current_speed() == Speed::Double;
349
350 let cpu_clocks_added = ((!double_speed) as u8) + 1;
352
353 let t_clocks = cpu_clocks_added * 2;
355
356 self.elapsed_ppu_cycles = self.elapsed_ppu_cycles.saturating_add(t_clocks as u32);
359
360 if self.stopped {
363 if self.joypad.get_keys_pressed() != 0xF {
364 self.stopped = false;
365 }
366
367 return;
368 }
369
370 self.cartridge.clock_mapper();
373 if !double_speed {
374 self.cartridge.clock_mapper();
375 }
376
377 self.ppu.clock(&mut self.interrupts, t_clocks);
379
380 self.apu.clock(double_speed, self.timer.read_div());
384
385 if self.hdma.is_transferreing(&self.ppu) {
387 let mut values = [0; 2];
390 let addr = self.hdma.get_next_src_address();
391 values[0] = self.read_not_ticked(addr, None);
392 let mut values_len = 1;
393 if !double_speed {
394 values_len = 2;
395 let addr = self.hdma.get_next_src_address();
396 values[1] = self.read_not_ticked(addr, None);
397 }
398
399 self.hdma
400 .transfer_clock(&mut self.ppu, &values[..values_len]);
401 }
402
403 self.timer.clock_divider(&mut self.interrupts);
406 self.joypad.update_interrupts(&mut self.interrupts);
407
408 let serial_bit = self.serial.clock_for_bit(&mut self.interrupts);
409
410 if let Some(bit) = serial_bit {
413 if let Some(serial_device) = self.serial_device.as_mut() {
414 if let Ok(mut serial_device) = serial_device.try_borrow_mut() {
415 let received_bit = serial_device.exchange_bit_external_clock(bit);
416 self.serial.receive_bit(received_bit);
417 }
418 }
419 }
420
421 if self.oam_dma.in_transfer() {
422 let value = self.read_not_ticked(self.oam_dma.get_next_address(), None);
423 self.oam_dma.transfer_clock(&mut self.ppu, value);
424 }
425 }
426
427 pub(crate) fn read_not_ticked(&mut self, addr: u16, block_for_dma: Option<BusType>) -> u8 {
428 let dma_value = if block_for_dma.is_some() {
429 self.oam_dma.current_value()
430 } else {
431 0xFF
432 };
433
434 let page = (addr >> 8) as u8;
435 let offset = addr as u8;
436
437 match (page, block_for_dma) {
438 (0x00, _) | (0x02..=0x08, _) if self.boot_rom.enabled => {
439 self.boot_rom.data[addr as usize]
440 } (0x02..=0x08, _) if self.boot_rom.enabled && !self.config.is_dmg => {
442 self.boot_rom.data[addr as usize]
443 } (0x00..=0x7F, Some(BusType::External)) => dma_value, (0x00..=0x3F, _) => self.cartridge.read_rom0(addr), (0x40..=0x7F, _) => self.cartridge.read_romx(addr), (0x80..=0x9F, Some(BusType::Video)) => dma_value, (0x80..=0x9F, _) => self.ppu.read_vram(addr), (0xA0..=0xDF, Some(BusType::External)) if self.config.is_dmg => dma_value, (0xA0..=0xBF, _) => self.cartridge.read_ram(addr), (0xC0..=0xCF, _) => self.wram.read_wram0(addr), (0xD0..=0xDF, _) => self.wram.read_wramx(addr), (0xE0..=0xFD, _) => self.read_not_ticked(0xC000 | (addr & 0x1FFF), block_for_dma), (0xFE, None) if offset <= 0x9F => self.ppu.read_oam(addr), (0xFE, _) if offset >= 0xA0 => 0, (0xFF, _) => self.read_io(offset), _ => 0xFF,
458 }
459 }
460
461 fn write_not_ticked(&mut self, addr: u16, data: u8, block_for_dma: Option<BusType>) {
462 let page = (addr >> 8) as u8;
463 let offset = addr as u8;
464
465 match (page, block_for_dma) {
466 (0x00..=0x7F, Some(BusType::External)) => {} (0x00..=0x7F, _) => self.cartridge.write_to_bank_controller(addr, data), (0x80..=0x9F, Some(BusType::Video)) => {} (0x80..=0x9F, _) => self.ppu.write_vram(addr, data), (0xA0..=0xDF, Some(BusType::External)) if self.config.is_dmg => {} (0xA0..=0xBF, _) => self.cartridge.write_ram(addr, data), (0xC0..=0xCF, _) => self.wram.write_wram0(addr, data), (0xD0..=0xDF, _) => self.wram.write_wramx(addr, data), (0xE0..=0xFD, _) => {
475 self.write_not_ticked(0xC000 | (addr & 0x1FFF), data, block_for_dma)
476 } (0xFE, None) if offset <= 0x9F => {
478 self.ppu.write_oam(addr, data) }
480 (0xFF, _) => self.write_io(offset, data), _ => {}
482 }
483 }
484
485 fn read_io(&mut self, offset: u8) -> u8 {
486 let addr = 0xFF00 | (offset as u16);
487 match offset {
488 0x00 => self.joypad.read_joypad(), 0x01 => self.serial.read_data(), 0x02 => self.serial.read_control(), 0x04 => self.timer.read_div(), 0x05 => self.timer.read_timer_counter(), 0x06 => self.timer.read_timer_reload(), 0x07 => self.timer.read_control(), 0x0F => self.interrupts.read_interrupt_flags(), 0x10..=0x3F => self.apu.read_register(addr), 0x40 => self.ppu.read_lcd_control(), 0x41 => self.ppu.read_lcd_status(), 0x42 => self.ppu.read_scroll_y(), 0x43 => self.ppu.read_scroll_x(), 0x44 => self.ppu.read_ly(), 0x45 => self.ppu.read_lyc(), 0x46 => self.oam_dma.read_register(), 0x47 => self.ppu.read_dmg_bg_palette(), 0x48 => self.ppu.read_dmg_sprite_palettes(0), 0x49 => self.ppu.read_dmg_sprite_palettes(1), 0x4A => self.ppu.read_window_y(), 0x4B => self.ppu.read_window_x(), 0x4D if self.lock.is_cgb_mode() => self.speed_controller.read_key1(), 0x4F if !self.config.is_dmg => self.ppu.read_vram_bank(), 0x50 => 0xFF, 0x51..=0x55 if self.lock.is_cgb_mode() => self.hdma.read_register(addr), 0x56 if self.lock.is_cgb_mode() => {
514 0xFF
516 }
517 0x68 if !self.config.is_dmg => self.ppu.read_cgb_bg_palettes_index(), 0x69 if !self.config.is_dmg => self.ppu.read_cgb_bg_palettes_data(), 0x6A if !self.config.is_dmg => self.ppu.read_cgb_sprite_palettes_index(), 0x6B if self.lock.is_cgb_mode() => self.ppu.read_cgb_sprite_palettes_data(), 0x6C if !self.config.is_dmg => self.ppu.read_sprite_priority_mode(),
522 0x70 if self.lock.is_cgb_mode() => self.wram.get_wram_bank(), 0x72 if !self.config.is_dmg => self.unknown_registers[0].read(), 0x73 if !self.config.is_dmg => self.unknown_registers[1].read(), 0x74 if self.lock.is_cgb_mode() => self.unknown_registers[2].read(), 0x75 if !self.config.is_dmg => self.unknown_registers[3].read(), 0x76 if !self.config.is_dmg => self.apu.read_pcm12(), 0x77 if !self.config.is_dmg => self.apu.read_pcm34(), 0x80..=0xFE => self.hram[addr as usize & 0x7F], 0xFF => self.interrupts.read_interrupt_enable(), _ => 0xFF,
532 }
533 }
534
535 fn write_io(&mut self, offset: u8, data: u8) {
536 let addr = 0xFF00 | (offset as u16);
537
538 match offset {
539 0x00 => self.joypad.write_joypad(data), 0x01 => self.serial.write_data(data), 0x02 => self.serial.write_control(data), 0x04 => self.timer.write_div(data), 0x05 => self.timer.write_timer_counter(data), 0x06 => self.timer.write_timer_reload(data), 0x07 => self.timer.write_control(data), 0x0F => self.interrupts.write_interrupt_flags(data), 0x10..=0x3F => self.apu.write_register(addr, data), 0x40 => self.ppu.write_lcd_control(data), 0x41 => self.ppu.write_lcd_status(data), 0x42 => self.ppu.write_scroll_y(data), 0x43 => self.ppu.write_scroll_x(data), 0x44 => self.ppu.write_ly(data), 0x45 => self.ppu.write_lyc(data), 0x46 => self.oam_dma.write_register(data), 0x47 => self.ppu.write_dmg_bg_palette(data), 0x48 => self.ppu.write_dmg_sprite_palettes(0, data), 0x49 => self.ppu.write_dmg_sprite_palettes(1, data), 0x4A => self.ppu.write_window_y(data), 0x4B => self.ppu.write_window_x(data), 0x4C if self.lock.is_cgb_mode() => self.lock.write(data), 0x4D if self.lock.is_cgb_mode() => self.speed_controller.write_key1(data), 0x4F if self.lock.is_cgb_mode() => self.ppu.write_vram_bank(data), 0x50 => {
564 self.lock.finish_boot();
565 self.boot_rom.enabled = false;
566 self.ppu
567 .update_cgb_mode(self.cartridge.is_cartridge_color());
568 } 0x51..=0x55 if self.lock.is_cgb_mode() => self.hdma.write_register(addr, data), 0x56 => {
571 }
573 0x68 if self.lock.is_cgb_mode() => self.ppu.write_cgb_bg_palettes_index(data), 0x69 if self.lock.is_cgb_mode() => self.ppu.write_cgb_bg_palettes_data(data), 0x6A if self.lock.is_cgb_mode() => self.ppu.write_cgb_sprite_palettes_index(data), 0x6B if self.lock.is_cgb_mode() => self.ppu.write_cgb_sprite_palettes_data(data), 0x6C if self.lock.is_cgb_mode() => self.ppu.write_sprite_priority_mode(data),
578 0x70 if self.lock.is_cgb_mode() => self.wram.set_wram_bank(data), 0x72 if !self.config.is_dmg => self.unknown_registers[0].write(data), 0x73 if !self.config.is_dmg => self.unknown_registers[1].write(data), 0x74 if self.lock.is_cgb_mode() => self.unknown_registers[2].write(data), 0x75 if !self.config.is_dmg => self.unknown_registers[3].write(data), 0x80..=0xFE => self.hram[addr as usize & 0x7F] = data, 0xFF => self.interrupts.write_interrupt_enable(data), _ => {}
586 }
587 }
588}
589
590impl CpuBusProvider for Bus {
591 fn read(&mut self, addr: u16) -> u8 {
593 let result = self.read_no_oam_bug(addr);
594
595 if self.config.is_dmg && addr & 0xFF00 == 0xFE00 {
596 self.ppu.oam_bug_read();
597 }
598
599 result
600 }
601
602 fn write(&mut self, addr: u16, data: u8) {
604 self.write_not_ticked(addr, data, self.oam_dma.conflicting_bus());
605 self.on_cpu_machine_cycle();
606
607 if self.config.is_dmg && addr & 0xFF00 == 0xFE00 {
608 self.ppu.oam_bug_write();
609 }
610 }
611
612 fn take_next_interrupt(&mut self) -> Option<InterruptType> {
614 let int = self.interrupts.get_highest_interrupt();
615 if let Some(int) = int {
616 self.interrupts.acknowledge_interrupt(int);
617 }
618 int
619 }
620
621 fn peek_next_interrupt(&mut self) -> Option<InterruptType> {
622 self.interrupts.get_highest_interrupt()
623 }
624
625 fn is_hdma_running(&mut self) -> bool {
626 self.hdma.is_transferreing(&self.ppu)
627 }
628
629 fn enter_stop_mode(&mut self) {
630 if self.speed_controller.preparing_switch() {
631 assert!(!self.config.is_dmg, "Cannot switch speed in DMG");
632 self.speed_controller.commit_speed_switch();
633 self.timer.write_div(0);
634 } else {
635 self.stopped = true;
636 self.ppu.enter_stop_mode();
637 }
641 }
642
643 fn stopped(&self) -> bool {
644 self.stopped
645 }
646
647 fn trigger_write_oam_bug(&mut self, addr: u16) {
648 if self.config.is_dmg && addr & 0xFF00 == 0xFE00 {
649 self.ppu.oam_bug_write();
650 }
651 }
652
653 fn trigger_read_write_oam_bug(&mut self, addr: u16) {
654 if self.config.is_dmg && addr & 0xFF00 == 0xFE00 {
655 self.ppu.oam_bug_read_write();
656 }
657 }
658
659 fn read_no_oam_bug(&mut self, addr: u16) -> u8 {
660 let result = self.read_not_ticked(addr, self.oam_dma.conflicting_bus());
661 self.on_cpu_machine_cycle();
662 result
663 }
664}