1use bitvec::prelude::*;
2use log::{debug, info, trace, warn};
3use serde::{Deserialize, Serialize};
4use std::cmp::max;
5
6use crate::{
7 config, context,
8 io::Io,
9 mbc::{Mbc, MbcTrait},
10 ppu,
11 util::{pack, trait_alias},
12};
13
14#[derive(Serialize, Deserialize)]
15pub struct Bus {
16 #[serde(with = "serde_bytes")]
17 ram: Vec<u8>,
18 ram_bank: u8,
19 #[serde(with = "serde_bytes")]
20 hiram: Vec<u8>,
21 #[serde(with = "serde_bytes")]
22 boot_rom: Option<Vec<u8>>,
23 map_boot_rom: bool,
24 vram_bank: u8,
25 current_speed: u8,
26 prepare_speed_switch: u8,
27 switch_delay: u16,
28 mbc: Mbc,
29 io: Io,
30 dma: Dma,
31 hdma: Hdma,
32 reg_ff72: u8,
34 reg_ff73: u8,
35 reg_ff75: u8,
36}
37
38trait_alias!(pub trait Context =
39 context::Rom + context::ExternalRam + context::Vram +
40 context::Oam + context::Ppu + context::Apu + context::InterruptFlag + context::Model);
41
42#[derive(Default, Serialize, Deserialize)]
43struct Dma {
44 source: u8,
45 pos: u8,
46 enabled: bool,
47 delay: usize,
48}
49
50#[derive(Default, Serialize, Deserialize)]
51struct Hdma {
52 source: u16,
53 dest: u16,
54 mode: HdmaMode,
55 length: u8,
56 enabled_general_dma: bool,
57 enabled_hblank_dma: bool,
58 prev_hblank: bool,
59}
60
61#[derive(Serialize, Deserialize)]
62enum HdmaMode {
63 General,
64 HBlank,
65}
66
67impl Default for HdmaMode {
68 fn default() -> Self {
69 HdmaMode::General
70 }
71}
72
73impl Bus {
74 pub fn new(model: config::Model, mbc: Mbc, boot_rom: &Option<Vec<u8>>, io: Io) -> Self {
75 if let Some(boot_rom) = boot_rom {
76 if !model.is_cgb() {
77 assert_eq!(boot_rom.len(), 0x100, "DMG Boot ROM must be 256 bytes");
78 } else {
79 assert_eq!(boot_rom.len(), 0x900, "CGB Boot ROM must be 2304 bytes");
80 }
81 }
82
83 let ram_size = if model.is_cgb() { 0x8000 } else { 0x2000 };
84
85 Self {
86 ram: vec![0; ram_size],
87 ram_bank: 1,
88 hiram: vec![0; 0x7F],
89 boot_rom: boot_rom.clone(),
90 map_boot_rom: boot_rom.is_some(),
91 vram_bank: 0,
92 current_speed: 0,
93 prepare_speed_switch: 0,
94 switch_delay: 0,
95 mbc,
96 io,
97 dma: Dma::default(),
98 hdma: Hdma::default(),
99 reg_ff72: 0,
100 reg_ff73: 0,
101 reg_ff75: 0,
102 }
103 }
104
105 pub fn read(&mut self, ctx: &mut impl Context, addr: u16) -> u8 {
106 let data = self.read_(ctx, addr);
107 trace!("<-- Read: ${addr:04X} = ${data:02X}");
108 data
109 }
110
111 pub fn read_immutable(&mut self, ctx: &mut impl Context, addr: u16) -> Option<u8> {
112 match addr {
113 0xFF00..=0xFF7F | 0xFFFF => None,
114 _ => Some(self.read_(ctx, addr)),
115 }
116 }
117
118 fn read_(&mut self, ctx: &mut impl Context, addr: u16) -> u8 {
119 match addr {
120 0x0100..=0x01FF => self.mbc.read(ctx, addr),
121 0x0000..=0x08FF => {
122 let is_boot_rom = self.map_boot_rom
123 && self
124 .boot_rom
125 .as_ref()
126 .map_or(false, |r| r.len() > addr as usize);
127 if is_boot_rom {
128 if let Some(boot_rom) = &self.boot_rom {
129 boot_rom[addr as usize]
130 } else {
131 !0
132 }
133 } else {
134 self.mbc.read(ctx, addr)
135 }
136 }
137 0x0900..=0x7FFF => self.mbc.read(ctx, addr),
138 0x8000..=0x9FFF => {
139 ctx.vram()[((addr & 0x1FFF) | (self.vram_bank as u16 * 0x2000)) as usize]
140 }
141 0xA000..=0xBFFF => self.mbc.read(ctx, addr),
142 0xC000..=0xFDFF => {
143 let bank = addr & 0x1000;
144 self.ram[((addr & 0x0FFF) + bank * self.ram_bank as u16) as usize]
145 }
146 0xFE00..=0xFE9F => {
147 if !self.dma.enabled {
148 ctx.oam()[(addr & 0xff) as usize]
149 } else {
150 !0
151 }
152 }
153 0xFEA0..=0xFEFF => {
154 warn!("Read from Unusable address: ${addr:04x}");
155 !0
156 }
157 0xFF46 => self.dma.source, 0xFF50 => !0, 0xFF4C => !0, 0xFF4D => {
164 if ctx.running_mode().is_cgb() {
165 pack! {
166 7..=7 => self.current_speed,
167 1..=6 => !0,
168 0..=0 => self.prepare_speed_switch,
169 }
170 } else {
171 !0
172 }
173 }
174 0xFF4E => !0, 0xFF4F => {
178 if ctx.running_mode().is_cgb() {
179 pack!(0..=0 => self.vram_bank, 1..=7 => !0)
180 } else {
181 !0
182 }
183 }
184
185 0xFF51 => {
186 warn!("Load HDMA1");
187 !0
188 } 0xFF52 => {
190 warn!("Load HDMA2");
191 !0
192 } 0xFF53 => {
194 warn!("Load HDMA3");
195 !0
196 } 0xFF54 => {
198 warn!("Load HDMA4");
199 !0
200 } 0xFF55 => {
204 if ctx.running_mode().is_cgb() {
205 pack! {
206 7 => !self.hdma.enabled_hblank_dma,
207 0..=6 => self.hdma.length,
208 }
209 } else {
210 !0
211 }
212 }
213 0xFF70 => {
215 if ctx.running_mode().is_cgb() {
216 pack!(0..=2 => self.ram_bank, 3..=7 => !0)
217 } else {
218 !0
219 }
220 }
221
222 0xFF72 => self.reg_ff72,
224 0xFF73 => self.reg_ff73,
225 0xFF75 => pack! {
226 7..=7 => !0,
227 4..=6 => self.reg_ff75,
228 0..=3 => !0,
229 },
230
231 0xFF00..=0xFF7F => self.io.read(ctx, addr),
232 0xFF80..=0xFFFE => self.hiram[(addr & 0x7F) as usize],
233 0xFFFF => self.io.read(ctx, addr),
234 }
235 }
236
237 pub fn write(&mut self, ctx: &mut impl Context, addr: u16, data: u8) {
238 trace!("--> Write: ${addr:04X} = ${data:02X}");
239 match addr {
240 0x0000..=0x7FFF => self.mbc.write(ctx, addr, data),
241 0x8000..=0x9FFF => {
242 ctx.vram_mut()[((addr & 0x1FFF) | (self.vram_bank as u16 * 0x2000)) as usize] = data
243 }
244 0xA000..=0xBFFF => self.mbc.write(ctx, addr, data),
245 0xC000..=0xFDFF => {
246 let bank = addr & 0x1000;
247 self.ram[((addr & 0x0FFF) + bank * self.ram_bank as u16) as usize] = data;
248 }
249 0xFE00..=0xFE9F => {
250 if !self.dma.enabled && !ctx.oam_lock() {
251 ctx.oam_mut()[(addr & 0xff) as usize] = data;
252 }
253 }
254 0xFEA0..=0xFEFF => {
255 }
257
258 0xFF46 => {
259 self.dma.source = data;
261 self.dma.pos = 0;
262 self.dma.enabled = false;
263 self.dma.delay = 2;
264 }
265 0xFF50 => self.map_boot_rom = data & 1 == 0, 0xFF4C => {
269 if ctx.model().is_cgb() {
270 let mode = match data.view_bits::<Lsb0>()[2..=3].load::<u8>() {
271 0 => context::RunningMode::Cgb,
272 1 => context::RunningMode::Dmg,
273 2 => context::RunningMode::Pgb1,
274 3 => context::RunningMode::Pgb2,
275 _ => unreachable!(),
276 };
277
278 ctx.set_running_mode(mode);
279 debug!("KEY0: CGB mode changed: {mode:?}");
280 } else {
281 warn!("KEY0 write on non-CGB");
282 }
283 }
284
285 0xFF4D => {
287 if ctx.model().is_cgb() {
288 debug!("KEY1: {data:02X}");
289 self.prepare_speed_switch = data & 1;
290 } else {
291 warn!("KEY1 write on non-CGB");
292 }
293 }
294
295 0xFF4F => {
297 if ctx.model().is_cgb() {
298 self.vram_bank = data & 1;
299 } else {
300 warn!("VBK write on non-CGB");
301 }
302 }
303 0xFF51 => {
305 if ctx.model().is_cgb() {
306 self.hdma.source.view_bits_mut::<Lsb0>()[8..=15].store(data);
307 } else {
308 warn!("HDMA1 write on non-CGB");
309 }
310 }
311 0xFF52 => {
313 if ctx.model().is_cgb() {
314 self.hdma.source.view_bits_mut::<Lsb0>()[0..=7].store(data & !0xf);
315 } else {
316 warn!("HDMA2 write on non-CGB");
317 }
318 }
319 0xFF53 => {
321 if ctx.model().is_cgb() {
322 self.hdma.dest.view_bits_mut::<Lsb0>()[8..=12].store(data & 0x1f);
323 } else {
324 warn!("HDMA3 write on non-CGB");
325 }
326 }
327 0xFF54 => {
329 if ctx.model().is_cgb() {
330 self.hdma.dest.view_bits_mut::<Lsb0>()[0..=7].store(data & !0xf);
331 } else {
332 warn!("HDMA4 write on non-CGB");
333 }
334 }
335 0xFF55 => {
337 if ctx.model().is_cgb() {
338 let v = data.view_bits::<Lsb0>();
339
340 assert!(!self.hdma.enabled_general_dma);
341
342 if self.hdma.enabled_hblank_dma {
343 assert!(!v[7], "General DMA start on doing HBLANK DMA");
344 self.hdma.enabled_hblank_dma = false;
345 } else if v[7] {
346 self.hdma.enabled_hblank_dma = true;
348 self.hdma.length = v[0..=6].load();
349 } else {
350 self.hdma.enabled_general_dma = true;
352 self.hdma.length = v[0..=6].load();
353 }
354 } else {
355 warn!("HDMA5 write on non-CGB");
356 }
357 }
358
359 0xFF70 => {
361 if ctx.model().is_cgb() {
362 self.ram_bank = max(1, data & 0x7);
363 } else {
364 warn!("SVBK write on non-CGB");
365 }
366 }
367
368 0xFF72 => self.reg_ff72 = data,
370 0xFF73 => self.reg_ff73 = data,
371 0xFF75 => self.reg_ff75.view_bits_mut::<Lsb0>()[4..=6]
372 .store(data.view_bits::<Lsb0>()[4..=6].load::<u8>()),
373
374 0xFF00..=0xFF7F => self.io.write(ctx, addr, data),
375 0xFF80..=0xFFFE => self.hiram[(addr & 0x7f) as usize] = data,
376 0xFFFF => self.io.write(ctx, addr, data),
377 };
378 }
379
380 pub fn io(&mut self) -> &mut Io {
381 &mut self.io
382 }
383
384 pub fn mbc(&mut self) -> &mut Mbc {
385 &mut self.mbc
386 }
387
388 pub fn boot_rom(&self) -> &Option<Vec<u8>> {
389 &self.boot_rom
390 }
391
392 pub fn current_speed(&self) -> u8 {
393 self.current_speed
394 }
395
396 pub fn stop(&mut self) {
397 if self.prepare_speed_switch != 0 {
398 self.prepare_speed_switch = 0;
399 self.switch_delay = 2050;
400 }
401 }
402
403 pub fn tick(&mut self, ctx: &mut impl Context) {
404 self.process_dma(ctx);
405 self.process_hdma(ctx);
406
407 if self.switch_delay > 0 {
408 self.switch_delay -= 1;
409 if self.switch_delay == 0 {
410 info!(
411 "Switch speed: {} -> {}",
412 self.current_speed,
413 self.current_speed ^ 1
414 );
415 self.current_speed ^= 1;
416 ctx.wake();
417 }
418 }
419 }
420
421 fn process_dma(&mut self, ctx: &mut impl Context) {
422 if self.dma.delay > 0 {
423 self.dma.delay -= 1;
424 if self.dma.delay == 0 {
425 self.dma.enabled = true;
426 }
427 return;
428 }
429 if !self.dma.enabled {
430 return;
431 }
432 log::trace!(
433 "<-> DMA: ${:02X}{:02X} -> $FE{:02X}",
434 self.dma.source,
435 self.dma.pos,
436 self.dma.pos
437 );
438 let data = self.read_(ctx, (self.dma.source as u16) << 8 | self.dma.pos as u16);
439 ctx.oam_mut()[self.dma.pos as usize] = data;
440 self.dma.pos += 1;
441 if self.dma.pos == 0xA0 {
442 self.dma.enabled = false;
443 }
444 }
445
446 fn process_hdma(&mut self, ctx: &mut impl Context) {
447 assert!(!(self.hdma.enabled_general_dma && self.hdma.enabled_hblank_dma));
448
449 let cur_hblank = ctx.mode() == ppu::Mode::Hblank;
450 let enter_hblank = !self.hdma.prev_hblank && cur_hblank;
451 self.hdma.prev_hblank = cur_hblank;
452
453 if self.hdma.enabled_general_dma || (self.hdma.enabled_hblank_dma && enter_hblank) {
454 log::trace!("HDMA: ${:04X} -> ${:04X}", self.hdma.source, self.hdma.dest);
455 for i in 0..16 {
456 let dat = self.read_(ctx, self.hdma.source + i);
457 self.write(ctx, 0x8000 | (self.hdma.dest + i), dat);
458 }
459 self.hdma.source = self.hdma.source.wrapping_add(16);
460 self.hdma.dest = self.hdma.dest.wrapping_add(16);
461
462 let (new_length, ovf) = self.hdma.length.overflowing_sub(1);
463 self.hdma.length = new_length;
464 if ovf || self.hdma.dest >= 0x2000 {
465 self.hdma.enabled_general_dma = false;
466 self.hdma.enabled_hblank_dma = false;
467 self.hdma.dest &= 0x1FFF;
468 }
469
470 ctx.stall_cpu(if self.current_speed == 0 { 8 } else { 16 });
471 }
472 }
473}