1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
//! Module for using the four Direct Memory Access (DMA) units.
//!
//! The GBA has four DMA units, numbered 0 through 3. If you ever try to have
//! more than one active at once the lowest numbered DMA will take priority and
//! complete first. Any use of DMA halts the CPU's operation. DMA can also be
//! configured to activate automatically at certain times, and when configured
//! like that the CPU runs in between the automatic DMA activations. (This is
//! actually the intended method for doing sound.) Each DMA unit has an intended
//! use:
//!
//! * DMA0: highest priority, but can only read from internal memory.
//! * DMA1/DMA2: Intended for sound transfers.
//! * DMA3: Can be used to write into Game Pak ROM / FlashROM (not SRAM).
//!
//! ## DMA Anatomy
//!
//! Each DMA is utilized via a combination four IO registers:
//!
//! * **Source Address:** (`*const u32`) Where to read from. DMA0 can only read
//!   from internal memory, the other units can read from any non-SRAM memory.
//! * **Destination Address:** (`*mut u32`) Where to write to. DMA0/1/2 can only
//!   write to internal memory, DMA3 can write to any non-SRAM memory.
//! * **Word Count:** (`u16`) How many units to transfer. Despite being called
//!   "word count" you can also use DMA to transfer half-words. DMA0/1/2 are
//!   limited to a 14-bit counter value, DMA3 allowed the full 16-bit range to
//!   be used for the counter. Note that even when transferring half-words you
//!   MUST have both Source and Destination be 32-bit aligned.
//! * **Control:** (`DMAControlSetting`) This is one of those fiddly bit-flag
//!   registers with all sorts of settings. See the type for more info.
//!
//! Note that Source, Destination, and Count are all read-only, while the
//! Control is read/write. When a DMA unit is _Enabled_ it copies the relevent
//! Source, Destination, and Count values into its own internal registers (so a
//! second Enable will reuse the old values). If the DMA _Repeats_ it re-copies
//! the Count, and also the Destination if
//! `DMADestAddressControl::IncrementReload` is configured in the Control, but
//! not the Source.
//!
//! When the DMA completes the Enable bit will be cleared from the Control,
//! unless the Repeat bit is set in the Control, in which case the Enable bit is
//! left active and the DMA will automatically activate again at the right time
//! (depending on the Start Timing setting). You have to manually turn off the
//! correct bit to stop the DMA unit.
//!
//! ## Safety
//!
//! As you might have noticed by now, utilizing DMA can be very fiddly. It moves
//! around bytes with no concern for the type system, including the `Clone` and
//! `Copy` traits that Rust relies on. Use of DMA can be made _somewhat safe_
//! via wrapper methods (such as those we've provided), but it's fundamentally
//! an unsafe thing to use.
//!
//! ## DMA Can Cause Subtle Bugs
//!
//! Since the CPU is halted while DMA is active you can miss out on interrupts
//! that should have fired. This can cause any number of unintended effects. DMA
//! is primarily intended for loading large amounts of graphical data from ROM,
//! or loading sound data at regulated intervals to avoid pops and crackles. It
//! _can_ be used for general purpose bulk transfers but you are advised to use
//! restraint.

use super::*;

newtype! {
  /// Allows you to configure a DMA unit.
  DMAControlSetting, u16
}
#[allow(missing_docs)]
impl DMAControlSetting {
  phantom_fields! {
    self.0: u16,
    dest_address_control: 5-6=DMADestAddressControl<Increment, Decrement, Fixed, IncrementReload>,
    source_address_control: 7-8=DMASrcAddressControl<Increment, Decrement, Fixed>,
    dma_repeat: 9,
    use_32bit: 10,
    start_time: 12-13=DMAStartTiming<Immediate, VBlank, HBlank, Special>,
    irq_when_done: 14,
    enabled: 15,
  }
}

/// Sets how the destination address should be adjusted per data transfer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum DMADestAddressControl {
  /// Offset +1
  Increment = 0,
  /// Offset -1
  Decrement = 1,
  /// No change
  Fixed = 2,
  /// Offset +1 per transfer and auto-reset to base when the DMA repeats.
  IncrementReload = 3,
}

/// Sets how the source address should be adjusted per data transfer.
///
/// Note that only 0,1,2 are allowed, 3 is prohibited.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum DMASrcAddressControl {
  /// Offset +1
  Increment = 0,
  /// Offset -1
  Decrement = 1,
  /// No change
  Fixed = 2,
}

/// Sets when the DMA should activate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum DMAStartTiming {
  /// Causes the DMA to start as soon as possible (2 wait cycles after enabled)
  Immediate = 0,
  /// Start at VBlank
  VBlank = 1,
  /// Start at HBlank
  HBlank = 2,
  /// The special timing depends on the DMA it's used with:
  /// * 0: Prohibited
  /// * 1/2: Sound FIFO,
  /// * 3: Video Capture, for transferring from memory/camera into VRAM
  Special = 3,
}

pub struct DMA0;
impl DMA0 {
  /// DMA 0 Source Address, read only.
  const DMA0SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00B0) };
  /// DMA 0 Destination Address, read only.
  const DMA0DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00B4) };
  /// DMA 0 Word Count, read only.
  const DMA0CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00B8) };
  /// DMA 0 Control, read/write.
  const DMA0CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00BA) };

  /// Assigns the source register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to read from.
  pub unsafe fn set_source(src: *const u32) {
    Self::DMA0SAD.write(src)
  }

  /// Assigns the destination register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to write to.
  pub unsafe fn set_dest(dest: *mut u32) {
    Self::DMA0DAD.write(dest)
  }

  /// Assigns the count register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The count given must specify a valid number of units to write, starting at
  /// the assigned destination address.
  pub unsafe fn set_count(count: u16) {
    Self::DMA0CNT_L.write(count)
  }

  /// Reads the current control setting.
  pub fn control() -> DMAControlSetting {
    Self::DMA0CNT_H.read()
  }

  /// Writes the control setting given.
  ///
  /// # Safety
  ///
  /// You must ensure that the Source, Destination, and Count values are set
  /// correctly **before** you activate the Enable bit.
  pub unsafe fn set_control(setting: DMAControlSetting) {
    Self::DMA0CNT_H.write(setting)
  }
}

pub struct DMA1;
impl DMA1 {
  /// DMA 1 Source Address, read only.
  const DMA1SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00BC) };
  /// DMA 1 Destination Address, read only.
  const DMA1DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00C0) };
  /// DMA 1 Word Count, read only.
  const DMA1CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00C4) };
  /// DMA 1 Control, read/write.
  const DMA1CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00C6) };

  /// Assigns the source register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to read from.
  pub unsafe fn set_source(src: *const u32) {
    Self::DMA1SAD.write(src)
  }

  /// Assigns the destination register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to write to.
  pub unsafe fn set_dest(dest: *mut u32) {
    Self::DMA1DAD.write(dest)
  }

  /// Assigns the count register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The count given must specify a valid number of units to write, starting at
  /// the assigned destination address.
  pub unsafe fn set_count(count: u16) {
    Self::DMA1CNT_L.write(count)
  }

  /// Reads the current control setting.
  pub fn control() -> DMAControlSetting {
    Self::DMA1CNT_H.read()
  }

  /// Writes the control setting given.
  ///
  /// # Safety
  ///
  /// You must ensure that the Source, Destination, and Count values are set
  /// correctly **before** you activate the Enable bit.
  pub unsafe fn set_control(setting: DMAControlSetting) {
    Self::DMA1CNT_H.write(setting)
  }
}

pub struct DMA2;
impl DMA2 {
  /// DMA 2 Source Address, read only.
  const DMA2SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00C8) };
  /// DMA 2 Destination Address, read only.
  const DMA2DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00CC) };
  /// DMA 2 Word Count, read only.
  const DMA2CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00D0) };
  /// DMA 2 Control, read/write.
  const DMA2CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00D2) };

  /// Assigns the source register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to read from.
  pub unsafe fn set_source(src: *const u32) {
    Self::DMA2SAD.write(src)
  }

  /// Assigns the destination register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to write to.
  pub unsafe fn set_dest(dest: *mut u32) {
    Self::DMA2DAD.write(dest)
  }

  /// Assigns the count register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The count given must specify a valid number of units to write, starting at
  /// the assigned destination address.
  pub unsafe fn set_count(count: u16) {
    Self::DMA2CNT_L.write(count)
  }

  /// Reads the current control setting.
  pub fn control() -> DMAControlSetting {
    Self::DMA2CNT_H.read()
  }

  /// Writes the control setting given.
  ///
  /// # Safety
  ///
  /// You must ensure that the Source, Destination, and Count values are set
  /// correctly **before** you activate the Enable bit.
  pub unsafe fn set_control(setting: DMAControlSetting) {
    Self::DMA2CNT_H.write(setting)
  }
}

/// This is the "general purpose" DMA unit, with the fewest limits.
pub struct DMA3;
impl DMA3 {
  /// DMA 3 Source Address, read only.
  const DMA3SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00D4) };
  /// DMA 3 Destination Address, read only.
  const DMA3DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00D8) };
  /// DMA 3 Word Count, read only.
  const DMA3CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00DC) };
  /// DMA 3 Control, read/write.
  const DMA3CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00DE) };

  /// Assigns the source register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to read from.
  pub unsafe fn set_source(src: *const u32) {
    Self::DMA3SAD.write(src)
  }

  /// Assigns the destination register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The source pointer must be aligned and valid to write to.
  pub unsafe fn set_dest(dest: *mut u32) {
    Self::DMA3DAD.write(dest)
  }

  /// Assigns the count register.
  ///
  /// This register is read only, so it is not exposed directly.
  ///
  /// # Safety
  ///
  /// The count given must specify a valid number of units to write, starting at
  /// the assigned destination address.
  pub unsafe fn set_count(count: u16) {
    Self::DMA3CNT_L.write(count)
  }

  /// Reads the current control setting.
  pub fn control() -> DMAControlSetting {
    Self::DMA3CNT_H.read()
  }

  /// Writes the control setting given.
  ///
  /// # Safety
  ///
  /// You must ensure that the Source, Destination, and Count values are set
  /// correctly **before** you activate the Enable bit.
  pub unsafe fn set_control(setting: DMAControlSetting) {
    Self::DMA3CNT_H.write(setting)
  }

  /// Fills `count` slots (starting at `dest`) with the value at `src`.
  ///
  /// # Safety
  ///
  /// Both pointers must be aligned, and all positions specified for writing
  /// must be valid for writing.
  pub unsafe fn fill32(src: *const u32, dest: *mut u32, count: u16) {
    const FILL_CONTROL: DMAControlSetting = DMAControlSetting::new()
      .with_source_address_control(DMASrcAddressControl::Fixed)
      .with_use_32bit(true)
      .with_enabled(true);
    // TODO: destination checking against SRAM
    Self::DMA3SAD.write(src);
    Self::DMA3DAD.write(dest);
    Self::DMA3CNT_L.write(count);
    Self::DMA3CNT_H.write(FILL_CONTROL);
    // Note(Lokathor): Once DMA is set to activate it takes 2 cycles for it to
    // kick in. You can do any non-DMA thing you like before that, but since
    // it's only two cycles we just insert two NOP instructions to ensure that
    // successive calls to `fill32` or other DMA methods don't interfere with
    // each other.
    asm!(/* ASM */ "NOP
                    NOP"
        :/* OUT */ // none
        :/* INP */ // none
        :/* CLO */ // none
        :/* OPT */ "volatile"
    );
  }
}