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" ); } }