bus.rsannotatedbus.rssource239 lines · 9.1 KB · raw
1//! Game Boy bus / address mapping
2
3use crate::HardwareMode;
4use crate::apu::Apu;
5use crate::cartridge::Cartridge;
6use crate::cgb::{CgbRegisters, CpuSpeed};
7use crate::dma::DmaUnit;
8use crate::inputs::InputState;
9use crate::interrupts::InterruptRegisters;
10use crate::memory::Memory;
11use crate::ppu::Ppu;
12use crate::serial::SerialPort;
13use crate::sm83::InterruptType;
14use crate::sm83::bus::BusInterface;
15use crate::timer::GbTimer;
16
17pub struct Bus<'a> {
18    pub hardware_mode: HardwareMode,
19    pub ppu: &'a mut Ppu,
20    pub apu: &'a mut Apu,
21    pub memory: &'a mut Memory,
22    pub serial_port: &'a mut SerialPort,
23    pub cartridge: &'a mut Cartridge,
24    pub interrupt_registers: &'a mut InterruptRegisters,
25    pub cgb_registers: &'a mut CgbRegisters,
26    pub timer: &'a mut GbTimer,
27    pub dma_unit: &'a mut DmaUnit,
28    pub input_state: &'a mut InputState,
29}
30
31fn cgb_only_read(bus: &Bus<'_>, read_fn: impl FnOnce(&Bus<'_>) -> u8) -> u8 {
32    match (bus.hardware_mode, bus.cgb_registers.dmg_compatibility) {
33        (HardwareMode::Dmg, _) | (HardwareMode::Cgb, true) => 0xFF,
34        (HardwareMode::Cgb, false) => read_fn(bus),
35    }
36}
37
38fn cgb_only_write(bus: &mut Bus<'_>, write_fn: impl FnOnce(&mut Bus<'_>)) {
39    // The CGB boot ROM depends on still being able to write to some CGB registers between writing
40    // to KEY0 (enable DMG compatibility mode) and writing to BANK (unmap boot ROM)
41    if bus.hardware_mode == HardwareMode::Cgb
42        && (!bus.cgb_registers.dmg_compatibility || bus.memory.boot_rom_mapped())
43    {
44        write_fn(bus);
45    }
46}
47
48fn cgb_boot_rom_only_write(bus: &mut Bus<'_>, write_fn: impl FnOnce(&mut Bus<'_>)) {
49    if bus.hardware_mode == HardwareMode::Cgb && bus.memory.boot_rom_mapped() {
50        write_fn(bus);
51    }
52}
53
54impl Bus<'_> {
55    fn read_io_register(&self, address: u16) -> u8 {
56        log::trace!("I/O register read: {address:04X}");
57
58        match address & 0x7F {
59            0x00 => self.input_state.read_joyp(),
60            0x01 => self.serial_port.read_data(),
61            0x02 => self.serial_port.read_control(),
62            0x04 => self.timer.read_div(),
63            0x05 => self.timer.read_tima(),
64            0x06 => self.timer.read_tma(),
65            0x07 => self.timer.read_tac(),
66            0x0F => self.interrupt_registers.read_if(),
67            0x10..=0x3F => self.apu.read_register(address),
68            0x40..=0x45 | 0x47..=0x4B => self.read_ppu_register(address),
69            0x4F | 0x68..=0x6B => cgb_only_read(self, |bus| bus.read_ppu_register(address)),
70            0x46 => self.dma_unit.read_dma_register(),
71            0x4D => cgb_only_read(self, |bus| bus.cgb_registers.read_key1()),
72            0x55 => cgb_only_read(self, |bus| bus.dma_unit.read_hdma5()),
73            0x6C => cgb_only_read(self, |bus| bus.cgb_registers.read_opri()),
74            0x70 => cgb_only_read(self, |bus| bus.memory.read_svbk()),
75            0x76 => cgb_only_read(self, |bus| bus.apu.read_pcm12()),
76            0x77 => cgb_only_read(self, |bus| bus.apu.read_pcm34()),
77            _ => {
78                log::debug!("Unexpected I/O register read: {address:04X}");
79                0xFF
80            }
81        }
82    }
83
84    fn read_ppu_register(&self, address: u16) -> u8 {
85        self.ppu.read_register(address, *self.cgb_registers)
86    }
87
88    fn write_io_register(&mut self, address: u16, value: u8) {
89        log::trace!("I/O register write: {address:04X} {value:02X}");
90
91        match address & 0x7F {
92            0x00 => self.input_state.write_joyp(value, self.interrupt_registers),
93            0x01 => self.serial_port.write_data(value),
94            0x02 => self.serial_port.write_control(value),
95            0x04 => self.timer.write_div(),
96            0x05 => self.timer.write_tima(value),
97            0x06 => self.timer.write_tma(value),
98            0x07 => self.timer.write_tac(value),
99            0x0F => self.interrupt_registers.write_if(value),
100            0x10..=0x3F => self.apu.write_register(address, value),
101            0x40..=0x45 | 0x47..=0x4B => self.write_ppu_register(address, value),
102            0x4F | 0x68..=0x6B => {
103                cgb_only_write(self, |bus| bus.write_ppu_register(address, value));
104            }
105            0x46 => self.dma_unit.write_dma_register(value),
106            0x4C => cgb_boot_rom_only_write(self, |bus| bus.cgb_registers.write_key0(value)),
107            0x4D => cgb_only_write(self, |bus| bus.cgb_registers.write_key1(value)),
108            0x50 => self.memory.write_bank(value),
109            0x51 => cgb_only_write(self, |bus| bus.dma_unit.write_hdma1(value)),
110            0x52 => cgb_only_write(self, |bus| bus.dma_unit.write_hdma2(value)),
111            0x53 => cgb_only_write(self, |bus| bus.dma_unit.write_hdma3(value)),
112            0x54 => cgb_only_write(self, |bus| bus.dma_unit.write_hdma4(value)),
113            0x55 => cgb_only_write(self, |bus| bus.dma_unit.write_hdma5(value, bus.ppu.mode())),
114            0x6C => cgb_boot_rom_only_write(self, |bus| bus.cgb_registers.write_opri(value)),
115            0x70 => cgb_only_write(self, |bus| bus.memory.write_svbk(value)),
116            _ => {
117                log::debug!("Unexpected I/O register write: {address:04X} {value:02X}");
118            }
119        }
120    }
121
122    fn write_ppu_register(&mut self, address: u16, value: u8) {
123        self.ppu.write_register(address, value, self.cgb_registers.speed, self.interrupt_registers);
124    }
125
126    fn tick_components(&mut self) {
127        loop {
128            self.timer.tick_m_cycle(self.interrupt_registers);
129            self.dma_unit.oam_dma_tick_m_cycle(self.cartridge, self.memory, self.ppu);
130            self.serial_port.tick(self.interrupt_registers);
131
132            if self.cgb_registers.speed == CpuSpeed::Double {
133                self.cgb_registers.double_speed_odd_cycle =
134                    !self.cgb_registers.double_speed_odd_cycle;
135                if self.cgb_registers.double_speed_odd_cycle {
136                    if self.dma_unit.vram_dma_active() {
137                        continue;
138                    }
139                    return;
140                }
141            }
142
143            for _ in 0..2 {
144                self.dma_unit.vram_dma_copy_byte(self.cartridge, self.memory, self.ppu);
145            }
146
147            for _ in 0..4 {
148                self.ppu.tick_dot(*self.cgb_registers, self.dma_unit, self.interrupt_registers);
149            }
150
151            self.apu.tick_m_cycle(self.timer, self.cgb_registers.speed);
152
153            self.cartridge.tick_cpu();
154
155            // Execute all non-CPU components indefinitely while CPU is halted due to VRAM DMA
156            // This allows HDMA to halt the CPU mid-instruction (fixes glitches in Toy Story Racer)
157            if !self.dma_unit.vram_dma_active() {
158                return;
159            }
160        }
161    }
162}
163
164impl BusInterface for Bus<'_> {
165    fn read(&mut self, address: u16) -> u8 {
166        self.tick_components();
167
168        match address {
169            0x0000..=0x7FFF => self
170                .memory
171                .try_read_boot_rom(address)
172                .unwrap_or_else(|| self.cartridge.read_rom(address)),
173            0x8000..=0x9FFF => self.ppu.read_vram(address),
174            0xA000..=0xBFFF => self.cartridge.read_ram(address),
175            0xC000..=0xFDFF => self.memory.read_main_ram(address),
176            0xFE00..=0xFE9F => {
177                if !self.dma_unit.oam_dma_in_progress() {
178                    self.ppu.read_oam(address)
179                } else {
180                    0xFF
181                }
182            }
183            // Unusable memory
184            0xFEA0..=0xFEFF => 0xFF,
185            0xFF00..=0xFF7F => self.read_io_register(address),
186            0xFF80..=0xFFFE => self.memory.read_hram(address),
187            0xFFFF => self.interrupt_registers.read_ie(),
188        }
189    }
190
191    fn write(&mut self, address: u16, value: u8) {
192        self.tick_components();
193
194        match address {
195            0x0000..=0x7FFF => self.cartridge.write_rom(address, value),
196            0x8000..=0x9FFF => self.ppu.write_vram(address, value),
197            0xA000..=0xBFFF => self.cartridge.write_ram(address, value),
198            0xC000..=0xFDFF => self.memory.write_main_ram(address, value),
199            0xFE00..=0xFE9F => {
200                if !self.dma_unit.oam_dma_in_progress() {
201                    self.ppu.write_oam(address, value);
202                }
203            }
204            // Unusable memory
205            0xFEA0..=0xFEFF => {}
206            0xFF00..=0xFF7F => self.write_io_register(address, value),
207            0xFF80..=0xFFFE => self.memory.write_hram(address, value),
208            0xFFFF => self.interrupt_registers.write_ie(value),
209        }
210    }
211
212    fn idle(&mut self) {
213        self.tick_components();
214    }
215
216    fn read_ie_register(&self) -> u8 {
217        self.interrupt_registers.read_ie() & 0x1F
218    }
219
220    fn read_if_register(&self) -> u8 {
221        self.interrupt_registers.read_if() & 0x1F
222    }
223
224    fn acknowledge_interrupt(&mut self, interrupt_type: InterruptType) {
225        self.interrupt_registers.clear_flag(interrupt_type);
226    }
227
228    fn halt(&self) -> bool {
229        self.dma_unit.vram_dma_active()
230    }
231
232    fn speed_switch_armed(&self) -> bool {
233        self.cgb_registers.speed_switch_armed
234    }
235
236    fn perform_speed_switch(&mut self) {
237        self.cgb_registers.perform_speed_switch();
238    }
239}