dma.rsannotateddma.rssource585 lines · 19.6 KB · raw

GBA DMA transfer state

The actual bus reads/writes are performed in [crate::bus::Bus::try_progress_dma]

5use crate::cartridge::Cartridge;
6use crate::interrupts::{InterruptRegisters, InterruptType};
7use bincode::{Decode, Encode};
8use jgenesis_common::num::{GetBit, U16Ext};
9use std::array;
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}