mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
Add basic dma effect stuff
This commit is contained in:
parent
43718773d7
commit
de15538d42
45
agb/examples/dma_effect.rs
Normal file
45
agb/examples/dma_effect.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use agb::{
|
||||
display::{
|
||||
example_logo,
|
||||
tiled::{RegularBackgroundSize, TileFormat},
|
||||
},
|
||||
interrupt::VBlank,
|
||||
};
|
||||
|
||||
#[agb::entry]
|
||||
fn main(mut gba: agb::Gba) -> ! {
|
||||
let (gfx, mut vram) = gba.display.video.tiled0();
|
||||
|
||||
let mut map = gfx.background(
|
||||
agb::display::Priority::P0,
|
||||
RegularBackgroundSize::Background32x32,
|
||||
TileFormat::FourBpp,
|
||||
);
|
||||
|
||||
let dma = gba.dma.dma().dma0;
|
||||
|
||||
example_logo::display_logo(&mut map, &mut vram);
|
||||
|
||||
let vblank = VBlank::get();
|
||||
|
||||
let offsets: Box<[_]> = (0..160 * 2).collect();
|
||||
|
||||
let mut frame = 0;
|
||||
|
||||
loop {
|
||||
let _transfer = unsafe { dma.hblank_transfer(&map.x_scroll_dma(), &offsets[frame..]) };
|
||||
|
||||
vblank.wait_for_vblank();
|
||||
frame += 1;
|
||||
if frame > 160 {
|
||||
frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ use crate::bitarray::Bitarray;
|
|||
use crate::display::affine::AffineMatrixBackground;
|
||||
use crate::display::tile_data::TileData;
|
||||
use crate::display::{Priority, DISPLAY_CONTROL};
|
||||
use crate::dma;
|
||||
use crate::fixnum::Vector2D;
|
||||
use crate::memory_mapped::MemoryMapped;
|
||||
|
||||
|
@ -294,6 +295,11 @@ impl RegularMap {
|
|||
self.scroll = pos;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn x_scroll_dma(&self) -> dma::DmaControllable<i16> {
|
||||
dma::DmaControllable::new(self.x_register().as_ptr())
|
||||
}
|
||||
|
||||
fn x_register(&self) -> MemoryMapped<i16> {
|
||||
unsafe { MemoryMapped::new(0x0400_0010 + 4 * self.background_id as usize) }
|
||||
}
|
||||
|
|
139
agb/src/dma.rs
139
agb/src/dma.rs
|
@ -1,5 +1,144 @@
|
|||
use core::{marker::PhantomData, mem::size_of, pin::Pin};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use crate::memory_mapped::MemoryMapped;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct DmaController {}
|
||||
|
||||
impl DmaController {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn dma(&mut self) -> Dmas<'_> {
|
||||
unsafe { Dmas::new() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dmas<'gba> {
|
||||
phantom: PhantomData<&'gba ()>,
|
||||
|
||||
pub dma0: Dma,
|
||||
pub dma3: Dma,
|
||||
}
|
||||
|
||||
impl<'gba> Dmas<'gba> {
|
||||
unsafe fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
|
||||
dma0: Dma::new(0),
|
||||
dma3: Dma::new(3),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dma {
|
||||
number: usize,
|
||||
|
||||
source_addr: MemoryMapped<u32>,
|
||||
dest_addr: MemoryMapped<u32>,
|
||||
ctrl_addr: MemoryMapped<u32>,
|
||||
}
|
||||
|
||||
impl Dma {
|
||||
unsafe fn new(number: usize) -> Self {
|
||||
Self {
|
||||
number,
|
||||
source_addr: unsafe { MemoryMapped::new(dma_source_addr(number)) },
|
||||
dest_addr: unsafe { MemoryMapped::new(dma_dest_addr(number)) },
|
||||
ctrl_addr: unsafe { MemoryMapped::new(dma_control_addr(number)) },
|
||||
}
|
||||
}
|
||||
|
||||
fn disable(&mut self) {
|
||||
unsafe { MemoryMapped::new(dma_control_addr(self.number)) }.set(0);
|
||||
}
|
||||
|
||||
pub unsafe fn hblank_transfer<'a, T>(
|
||||
&'a self,
|
||||
location: &DmaControllable<T>,
|
||||
values: &[T],
|
||||
) -> DmaTransferHandle<'a, T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
assert!(
|
||||
values.len() >= 160,
|
||||
"need to pass at least 160 values for a hblank_transfer"
|
||||
);
|
||||
let handle = unsafe { DmaTransferHandle::new(self.number, values) };
|
||||
|
||||
let n_transfers = (size_of::<T>() / 2) as u32;
|
||||
|
||||
self.source_addr.set(handle.data.as_ptr() as u32);
|
||||
self.dest_addr.set(location.memory_location as u32);
|
||||
|
||||
self.ctrl_addr.set(
|
||||
(0b10 << 0x15) | // keep destination address fixed
|
||||
// (0b00 << 0x17) | // increment the source address each time
|
||||
1 << 0x19 | // repeat the copy each hblank
|
||||
// 0 << 0x1a | // copy in half words (see n_transfers above)
|
||||
0b10 << 0x1c | // copy each hblank
|
||||
1 << 0x1f | // enable the dma
|
||||
n_transfers, // the number of halfwords to copy
|
||||
);
|
||||
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct to describe things you can modify using DMA (normally some register within the GBA)
|
||||
///
|
||||
/// This is generally used to perform fancy graphics tricks like screen wobble on a per-scanline basis or
|
||||
/// to be able to create a track like in mario kart. This is an advanced technique and likely not needed
|
||||
/// unless you want to do fancy graphics.
|
||||
pub struct DmaControllable<Item> {
|
||||
memory_location: *mut Item,
|
||||
}
|
||||
|
||||
impl<Item> DmaControllable<Item> {
|
||||
pub(crate) fn new(memory_location: *mut Item) -> Self {
|
||||
Self { memory_location }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DmaTransferHandle<'a, T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
number: usize,
|
||||
data: Pin<Box<[T]>>,
|
||||
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T> DmaTransferHandle<'a, T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
pub(crate) unsafe fn new(number: usize, data: &[T]) -> Self {
|
||||
Self {
|
||||
number,
|
||||
data: Box::into_pin(data.into()),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for DmaTransferHandle<'a, T>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
Dma::new(self.number).disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn dma_source_addr(dma: usize) -> usize {
|
||||
0x0400_00b0 + 0x0c * dma
|
||||
}
|
||||
|
|
|
@ -235,6 +235,8 @@ pub struct Gba {
|
|||
pub save: save::SaveManager,
|
||||
/// Manages access to the Game Boy Advance's 4 timers.
|
||||
pub timers: timer::TimerController,
|
||||
/// Manages access to the Game Boy Advance's DMA
|
||||
pub dma: dma::DmaController,
|
||||
}
|
||||
|
||||
impl Gba {
|
||||
|
@ -255,6 +257,7 @@ impl Gba {
|
|||
mixer: sound::mixer::MixerController::new(),
|
||||
save: save::SaveManager::new(),
|
||||
timers: timer::TimerController::new(),
|
||||
dma: dma::DmaController::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ impl<T> MemoryMapped<T> {
|
|||
unsafe { self.address.write_volatile(val) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_ptr(&self) -> *mut T {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MemoryMapped<T>
|
||||
|
|
Loading…
Reference in a new issue