GBA DMA transfer state
The actual bus reads/writes are performed in [crate::bus::Bus::try_progress_dma]
11const INITIAL_START_LATENCY: u64 = 2; 12 13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 14enum AddressIncrement { 15 #[default] 16 Increment = 0, 17 Decrement = 1, 18 Fixed = 2, 19 IncrementReload = 3, 20} 21 22impl AddressIncrement { 23 fn from_bits(bits: u16) -> Self { 24 match bits & 3 { 25 0 => Self::Increment, 26 1 => Self::Decrement, 27 2 => Self::Fixed, 28 3 => Self::IncrementReload, 29 _ => unreachable!("value & 3 is always <= 3"), 30 } 31 } 32 33 fn apply(self, address: u32, increment: u32) -> u32 { 34 match self { 35 Self::Increment | Self::IncrementReload => address.wrapping_add(increment), 36 Self::Decrement => address.wrapping_sub(increment), 37 Self::Fixed => address, 38 } 39 } 40} 41 42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 43pub enum TransferUnit { 44 #[default] 45 Halfword = 0, 46 Word = 1, 47} 48 49impl TransferUnit { 50 fn from_bit(bit: bool) -> Self { 51 if bit { Self::Word } else { Self::Halfword } 52 } 53 54 fn address_increment(self) -> u32 { 55 match self { 56 Self::Halfword => 2, 57 Self::Word => 4, 58 } 59 } 60} 61 62#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 63enum StartTiming { 64 #[default] 65 Immediate = 0, 66 VBlank = 1, 67 HBlank = 2, 68 Special = 3, 69} 70 71impl StartTiming { 72 fn from_bits(bits: u16) -> Self { 73 match bits & 3 { 74 0 => Self::Immediate, 75 1 => Self::VBlank, 76 2 => Self::HBlank, 77 3 => Self::Special, 78 _ => unreachable!("value & 3 is always <= 3"), 79 } 80 } 81} 82 83#[derive(Debug, Clone, Encode, Decode)] 84struct DmaChannel { 85 idx: u8, 86 last_read: u32, 87 source_address: u32, 88 source_addr_mask: u32, 89 destination_address: u32, 90 dest_addr_mask: u32, 91 length: u16, 92 length_mask: u16, 93 // Control register fields 94 source_increment: AddressIncrement, 95 destination_increment: AddressIncrement, 96 repeat: bool, 97 unit: TransferUnit, 98 start_timing: StartTiming, 99 irq_enabled: bool, 100 dma_enabled: bool, 101 game_pak_drq_enabled: bool, 102 // Internal latches used for in-progress DMA 103 latched_source_address: u32, 104 latched_destination_address: u32, 105 latched_length: u16, 106 dma_active: bool, 107 start_cycles: u64, 108} 109 110impl DmaChannel { 111 fn new(idx: u8) -> Self { 112 let length_mask = if idx != 3 { 113 // DMA0-2 have 14-bit length 114 0x3FFF 115 } else { 116 // DMA3 has 16-bit length 117 0xFFFF 118 }; 119 120 let (source_addr_mask, dest_addr_mask) = match idx { 121 0 => { 122 // DMA0 can only access $0000000-$7FFFFFF (can't access cartridge) 123 (0x7FFFFFF, 0x7FFFFFF) 124 } 125 1 | 2 => { 126 // DMA1-2 can read from $0000000-$FFFFFFF and write to $0000000-$7FFFFFF 127 // (can't write to cartridge) 128 (0xFFFFFFF, 0x7FFFFFF) 129 } 130 3 => { 131 // DMA3 can access all valid addresses (except BIOS ROM) 132 (0xFFFFFFF, 0xFFFFFFF) 133 } 134 _ => panic!("invalid DMA channel {idx}, must be 0-3"), 135 }; 136 137 Self { 138 idx, 139 last_read: 0, 140 source_address: 0, 141 source_addr_mask, 142 destination_address: 0, 143 dest_addr_mask, 144 length: 0, 145 length_mask, 146 source_increment: AddressIncrement::default(), 147 destination_increment: AddressIncrement::default(), 148 repeat: false, 149 unit: TransferUnit::default(), 150 start_timing: StartTiming::default(), 151 irq_enabled: false, 152 dma_enabled: false, 153 game_pak_drq_enabled: false, 154 latched_source_address: 0, 155 latched_destination_address: 0, 156 latched_length: 0, 157 dma_active: false, 158 start_cycles: u64::MAX, 159 } 160 } 161 162 // $40000B0: DMA0SAD_L (DMA0 source address low) 163 // $40000BC: DMA1SAD_L 164 // $40000C8: DMA2SAD_L 165 // $40000D4: DMA3SAD_L 166 fn write_source_low(&mut self, value: u16) { 167 self.source_address = (self.source_address & !0xFFFF) | u32::from(value); 168 169 log::trace!("DMA{}SAD_L write: {value:04X}", self.idx); 170 log::trace!(" Source address: {:08X}", self.source_address); 171 } 172 173 // $40000B2: DMA0SAD_H (DMA0 source address high) 174 // $40000BE: DMA1SAD_H 175 // $40000CA: DMA2SAD_H 176 // $40000D6: DMA3SAD_H 177 fn write_source_high(&mut self, value: u16) { 178 // Highest 4 bits are ignored 179 self.source_address = (self.source_address & 0xFFFF) | (u32::from(value & 0x0FFF) << 16); 180 181 log::trace!("DMA{}SAD_H write: {value:04X}", self.idx); 182 log::trace!(" Source address: {:08X}", self.source_address); 183 } 184 185 // $40000B4: DMA0DAD_L (DMA0 destination address low) 186 // $40000C0: DMA1DAD_L 187 // $40000CC: DMA2DAD_L 188 // $40000D8: DMA3DAD_L 189 fn write_destination_low(&mut self, value: u16) { 190 self.destination_address = (self.destination_address & !0xFFFF) | u32::from(value); 191 192 log::trace!("DMA{}DAD_L write: {value:04X}", self.idx); 193 log::trace!(" Destination address: {:08X}", self.destination_address); 194 } 195 196 // $40000B6: DMA0DAD_H (DMA0 destination address high) 197 // $40000C2: DMA1DAD_H 198 // $40000CE: DMA2DAD_H 199 // $40000DA: DMA3DAD_H 200 fn write_destination_high(&mut self, value: u16) { 201 // Highest 4 bits are ignored 202 self.destination_address = 203 (self.destination_address & 0xFFFF) | (u32::from(value & 0x0FFF) << 16); 204 205 log::trace!("DMA{}DAD_H write: {value:04X}", self.idx); 206 log::trace!(" Destination address: {:08X}", self.destination_address); 207 } 208 209 // $40000B8: DMA0CNT_L (DMA0 control low / length) 210 // $40000C4: DMA1CNT_L 211 // $40000D0: DMA2CNT_L 212 // $40000DC: DMA3CNT_L 213 fn write_length(&mut self, value: u16) { 214 self.length = value & self.length_mask; 215 216 log::trace!("DMA{}CNT_L write: {value:04X}", self.idx); 217 log::trace!(" Length: {:04X}", self.length); 218 } 219 220 // $40000BA: DMA0CNT_H (DMA0 control high) 221 // $40000C6: DMA1CNT_H 222 // $40000D2: DMA2CNT_H 223 // $40000DE: DMA3CNT_H 224 fn write_control(&mut self, value: u16, cycles: u64) { 225 self.destination_increment = AddressIncrement::from_bits(value >> 5); 226 self.source_increment = AddressIncrement::from_bits(value >> 7); 227 self.repeat = value.bit(9); 228 self.unit = TransferUnit::from_bit(value.bit(10)); 229 self.game_pak_drq_enabled = self.idx == 3 && value.bit(11); 230 self.start_timing = StartTiming::from_bits(value >> 12); 231 self.irq_enabled = value.bit(14); 232 233 let prev_dma_enabled = self.dma_enabled; 234 self.dma_enabled = value.bit(15); 235 236 if !prev_dma_enabled && self.dma_enabled { 237 log::trace!("DMA{} newly enabled", self.idx); 238 239 self.latched_source_address = self.source_address; 240 self.latched_destination_address = self.destination_address; 241 self.latched_length = self.effective_length(); 242 243 if self.start_timing == StartTiming::Immediate { 244 self.dma_active = true; 245 self.start_cycles = cycles + INITIAL_START_LATENCY; 246 } 247 } 248 249 log::trace!("DMA{}CNT_H write: {value:04X}", self.idx); 250 log::trace!(" Destination increment: {:?}", self.destination_increment); 251 log::trace!(" Source increment: {:?}", self.source_increment); 252 log::trace!(" Repeat: {}", self.repeat); 253 log::trace!(" Transfer unit: {:?}", self.unit); 254 log::trace!(" Start timing: {:?}", self.start_timing); 255 log::trace!(" IRQ enabled: {}", self.irq_enabled); 256 log::trace!(" DMA enabled: {}", self.dma_enabled); 257 log::trace!(" Game Pak DRQ enabled: {}", self.game_pak_drq_enabled); 258 } 259 260 // $40000BA: DMA0CNT_H (DMA0 control high) 261 // $40000C6: DMA1CNT_H 262 // $40000D2: DMA2CNT_H 263 // $40000DE: DMA3CNT_H 264 fn read_control(&self) -> u16 { 265 ((self.destination_increment as u16) << 5) 266 | ((self.source_increment as u16) << 7) 267 | (u16::from(self.repeat) << 9) 268 | ((self.unit as u16) << 10) 269 | (u16::from(self.game_pak_drq_enabled) << 11) 270 | ((self.start_timing as u16) << 12) 271 | (u16::from(self.irq_enabled) << 14) 272 | (u16::from(self.dma_enabled) << 15) 273 } 274 275 fn effective_length(&self) -> u16 { 276 // Audio FIFO DMA is always 4 words 277 match self.start_timing { 278 StartTiming::Special if self.idx != 3 => 4, 279 _ => self.length, 280 } 281 } 282 283 fn activate_if_matches( 284 &mut self, 285 any_active: &mut bool, 286 cycles: u64, 287 predicate: impl Fn(&Self) -> bool, 288 ) { 289 if self.dma_enabled && !self.dma_active && predicate(self) { 290 self.dma_active = true; 291 self.start_cycles = cycles + INITIAL_START_LATENCY; 292 *any_active = true; 293 } 294 } 295} 296 297#[derive(Debug, Clone, Copy)] 298pub struct DmaTransfer { 299 pub channel: u8, 300 pub source: Option<u32>, 301 pub destination: u32, 302 pub unit: TransferUnit, 303 pub read_latch: u32, 304} 305 306#[derive(Debug, Clone, Encode, Decode)] 307pub struct DmaState { 308 channels: [DmaChannel; 4], 309 any_active: bool, 310 any_start_latency: bool, 311} 312 313impl DmaState { 314 pub fn new() -> Self { 315 Self { 316 channels: array::from_fn(|ch| DmaChannel::new(ch as u8)), 317 any_active: false, 318 any_start_latency: false, 319 } 320 } 321 322 pub fn next_transfer( 323 &mut self, 324 interrupts: &mut InterruptRegisters, 325 cycles: u64, 326 ) -> Option<DmaTransfer> { 327 if !self.any_active { 328 return None; 329 } 330 331 for (i, channel) in self.channels.iter_mut().enumerate() { 332 if !channel.dma_active || channel.start_cycles > cycles { 333 continue; 334 } 335 336 // Audio FIFO DMA is always word-size 337 let audio_dma = channel.idx != 3 && channel.start_timing == StartTiming::Special; 338 let unit = if audio_dma { TransferUnit::Word } else { channel.unit }; 339 let increment = unit.address_increment(); 340 341 let source_address = channel.latched_source_address & channel.source_addr_mask; 342 let source_valid = source_address >= 0x2000000; 343 if source_valid { 344 // When DMA reads an invalid address, source address does not increment and the 345 // channel returns the last value that it read from a valid address 346 channel.latched_source_address = 347 channel.source_increment.apply(source_address, increment); 348 } 349 350 let destination = channel.latched_destination_address & channel.dest_addr_mask; 351 if !audio_dma && destination >= 0x2000000 { 352 // Destination address does not increment for audio FIFO DMA or invalid address writes 353 channel.latched_destination_address = 354 channel.destination_increment.apply(destination, increment); 355 } 356 357 let channel_idx = channel.idx; 358 let read_latch = channel.last_read; 359 360 channel.latched_length = channel.latched_length.wrapping_sub(1) & channel.length_mask; 361 if channel.latched_length == 0 { 362 // Ignore repeat bit for immediate start timing 363 // Several games depend on this (e.g. NFL Blitz 2002, Kong: The Animated Series) 364 channel.dma_enabled = 365 channel.repeat && channel.start_timing != StartTiming::Immediate; 366 channel.dma_active = false; 367 channel.latched_length = channel.effective_length(); 368 369 if channel.destination_increment == AddressIncrement::IncrementReload { 370 channel.latched_destination_address = channel.destination_address; 371 } 372 373 if channel.irq_enabled { 374 interrupts.set_flag(InterruptType::DMA[i], cycles); 375 } 376 377 self.update_any_active(); 378 } 379 380 return Some(DmaTransfer { 381 channel: channel_idx, 382 source: source_valid.then_some(source_address), 383 destination, 384 unit, 385 read_latch, 386 }); 387 } 388 389 None 390 } 391 392 fn update_any_active(&mut self) { 393 self.any_active = self.channels.iter().any(|channel| channel.dma_active); 394 } 395 396 pub fn update_read_latch_halfword(&mut self, idx: u8, value: u16) { 397 // Halfword reads duplicate the value in both low and high halfwords 398 // Lufia: The Ruins of Lore depends on this 399 let word = (u32::from(value) << 16) | u32::from(value); 400 self.channels[idx as usize].last_read = word; 401 } 402 403 pub fn update_read_latch_word(&mut self, idx: u8, value: u32) { 404 self.channels[idx as usize].last_read = value; 405 } 406 407 pub fn notify_vblank_start(&mut self, cycles: u64) { 408 for channel in &mut self.channels { 409 channel.activate_if_matches(&mut self.any_active, cycles, |channel| { 410 channel.start_timing == StartTiming::VBlank 411 }); 412 } 413 } 414 415 pub fn notify_hblank_start(&mut self, cycles: u64) { 416 for channel in &mut self.channels { 417 channel.activate_if_matches(&mut self.any_active, cycles, |channel| { 418 channel.start_timing == StartTiming::HBlank 419 }); 420 } 421 } 422 423 pub fn notify_apu_fifo_a(&mut self, cycles: u64) { 424 self.channels[1].activate_if_matches(&mut self.any_active, cycles, |channel| { 425 channel.start_timing == StartTiming::Special 426 }); 427 } 428 429 pub fn notify_apu_fifo_b(&mut self, cycles: u64) { 430 self.channels[2].activate_if_matches(&mut self.any_active, cycles, |channel| { 431 channel.start_timing == StartTiming::Special 432 }); 433 } 434 435 pub fn notify_video_capture(&mut self, cycles: u64) { 436 self.channels[3].activate_if_matches(&mut self.any_active, cycles, |channel| { 437 channel.start_timing == StartTiming::Special 438 }); 439 } 440 441 pub fn end_video_capture(&mut self) { 442 if self.channels[3].dma_enabled && self.channels[3].start_timing == StartTiming::Special { 443 self.channels[3].dma_enabled = false; 444 self.channels[3].dma_active = false; 445 } 446 self.update_any_active(); 447 } 448 449 pub fn video_capture_active(&self) -> bool { 450 self.channels[3].dma_enabled && self.channels[3].start_timing == StartTiming::Special 451 } 452 453 pub fn read_register(&self, address: u32) -> Option<u16> { 454 let value = match address { 455 0x40000B8 | 0x40000C4 | 0x40000D0 | 0x40000DC => { 456 // Low halfword of word-size control reads (length is not readable) 457 0 458 } 459 0x40000BA => self.channels[0].read_control(), 460 0x40000C6 => self.channels[1].read_control(), 461 0x40000D2 => self.channels[2].read_control(), 462 0x40000DE => self.channels[3].read_control(), 463 _ => { 464 log::debug!("Unexpected read from write-only DMA register {address:08X}"); 465 return None; 466 } 467 }; 468 469 Some(value) 470 } 471 472 pub fn write_register( 473 &mut self, 474 address: u32, 475 value: u16, 476 cycles: u64, 477 cartridge: &mut Cartridge, 478 ) { 479 debug_assert!((0x40000B0..0x40000E0).contains(&address)); 480 481 let dma_base_address = address - 0x40000B0; 482 let channel = (dma_base_address / 0xC) as usize; 483 let offset = dma_base_address % 0xC; 484 485 match offset { 486 0x0 => self.channels[channel].write_source_low(value), 487 0x2 => self.channels[channel].write_source_high(value), 488 0x4 => self.channels[channel].write_destination_low(value), 489 0x6 => self.channels[channel].write_destination_high(value), 490 0x8 => self.channels[channel].write_length(value), 491 0xA => { 492 if channel == 3 { 493 self.write_channel_3_control(value, cycles, cartridge); 494 } else { 495 self.channels[channel].write_control(value, cycles); 496 } 497 } 498 _ => { 499 log::error!("Invalid DMA register address: {address:08X} {value:04X}"); 500 } 501 } 502 503 self.update_any_active(); 504 } 505 506 // TODO I'm not sure any of this is correct 507 pub fn write_register_byte( 508 &mut self, 509 address: u32, 510 value: u8, 511 cycles: u64, 512 cartridge: &mut Cartridge, 513 ) { 514 fn set_byte(mut halfword: u16, i: u32, byte: u8) -> u16 { 515 if i & 1 == 0 { 516 halfword.set_lsb(byte); 517 } else { 518 halfword.set_msb(byte); 519 } 520 halfword 521 } 522 523 debug_assert!((0x40000B0..0x40000E0).contains(&address)); 524 525 log::debug!("DMA byte write: {address:08X} {value:02X}"); 526 527 let dma_base_address = address - 0x40000B0; 528 let channel = (dma_base_address / 0xC) as usize; 529 let offset = dma_base_address % 0xC; 530 531 match offset { 532 0x0 => { 533 let source_low = self.channels[channel].source_address as u16; 534 let source_low = set_byte(source_low, address, value); 535 self.channels[channel].write_source_low(source_low); 536 } 537 0x2 => { 538 let source_high = (self.channels[channel].source_address >> 16) as u16; 539 let source_high = set_byte(source_high, address, value); 540 self.channels[channel].write_source_high(source_high); 541 } 542 0x4 => { 543 let destination_low = self.channels[channel].destination_address as u16; 544 let destination_low = set_byte(destination_low, address, value); 545 self.channels[channel].write_destination_low(destination_low); 546 } 547 0x6 => { 548 let destination_high = (self.channels[channel].destination_address >> 16) as u16; 549 let destination_high = set_byte(destination_high, address, value); 550 self.channels[channel].write_destination_high(destination_high); 551 } 552 0x8 => { 553 let length = set_byte(self.channels[channel].length, address, value); 554 self.channels[channel].write_length(length); 555 } 556 0xA => { 557 let control = set_byte(self.channels[channel].read_control(), address, value); 558 if channel == 3 { 559 self.write_channel_3_control(control, cycles, cartridge); 560 } else { 561 self.channels[channel].write_control(control, cycles); 562 } 563 } 564 _ => { 565 log::error!("Invalid DMA register address: {address:08X} {value:02X}"); 566 } 567 } 568 } 569 570 fn write_channel_3_control(&mut self, value: u16, cycles: u64, cartridge: &mut Cartridge) { 571 let prev_enabled = self.channels[3].dma_enabled; 572 self.channels[3].write_control(value, cycles); 573 574 if !prev_enabled 575 && self.channels[3].dma_enabled 576 && (0xD000000..0xE000000).contains(&self.channels[3].destination_address) 577 { 578 cartridge.notify_dma_to_rom( 579 self.channels[3].destination_address, 580 self.channels[3].length, 581 self.channels[3].unit, 582 ); 583 } 584 } 585}