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::affine::AffineMatrixBackground;
|
||||||
use crate::display::tile_data::TileData;
|
use crate::display::tile_data::TileData;
|
||||||
use crate::display::{Priority, DISPLAY_CONTROL};
|
use crate::display::{Priority, DISPLAY_CONTROL};
|
||||||
|
use crate::dma;
|
||||||
use crate::fixnum::Vector2D;
|
use crate::fixnum::Vector2D;
|
||||||
use crate::memory_mapped::MemoryMapped;
|
use crate::memory_mapped::MemoryMapped;
|
||||||
|
|
||||||
|
@ -294,6 +295,11 @@ impl RegularMap {
|
||||||
self.scroll = pos;
|
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> {
|
fn x_register(&self) -> MemoryMapped<i16> {
|
||||||
unsafe { MemoryMapped::new(0x0400_0010 + 4 * self.background_id as usize) }
|
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;
|
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 {
|
const fn dma_source_addr(dma: usize) -> usize {
|
||||||
0x0400_00b0 + 0x0c * dma
|
0x0400_00b0 + 0x0c * dma
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,6 +235,8 @@ pub struct Gba {
|
||||||
pub save: save::SaveManager,
|
pub save: save::SaveManager,
|
||||||
/// Manages access to the Game Boy Advance's 4 timers.
|
/// Manages access to the Game Boy Advance's 4 timers.
|
||||||
pub timers: timer::TimerController,
|
pub timers: timer::TimerController,
|
||||||
|
/// Manages access to the Game Boy Advance's DMA
|
||||||
|
pub dma: dma::DmaController,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gba {
|
impl Gba {
|
||||||
|
@ -255,6 +257,7 @@ impl Gba {
|
||||||
mixer: sound::mixer::MixerController::new(),
|
mixer: sound::mixer::MixerController::new(),
|
||||||
save: save::SaveManager::new(),
|
save: save::SaveManager::new(),
|
||||||
timers: timer::TimerController::new(),
|
timers: timer::TimerController::new(),
|
||||||
|
dma: dma::DmaController::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ impl<T> MemoryMapped<T> {
|
||||||
unsafe { self.address.write_volatile(val) }
|
unsafe { self.address.write_volatile(val) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_ptr(&self) -> *mut T {
|
||||||
|
self.address
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> MemoryMapped<T>
|
impl<T> MemoryMapped<T>
|
||||||
|
|
Loading…
Reference in a new issue