diff --git a/agb/examples/dma_effect_circular_window.rs b/agb/examples/dma_effect_circular_window.rs new file mode 100644 index 00000000..d98e6f60 --- /dev/null +++ b/agb/examples/dma_effect_circular_window.rs @@ -0,0 +1,97 @@ +#![no_std] +#![no_main] +#![feature(isqrt)] + +extern crate alloc; + +use agb::{ + display::{ + example_logo, + tiled::{RegularBackgroundSize, TileFormat}, + window::WinIn, + HEIGHT, WIDTH, + }, + fixnum::{Num, Rect, Vector2D}, + interrupt::VBlank, +}; +use alloc::{boxed::Box, vec}; + +type FNum = Num; + +#[agb::entry] +fn entry(mut gba: agb::Gba) -> ! { + main(gba) +} + +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 mut window = gba.display.window.get(); + window + .win_in(WinIn::Win0) + .set_background_enable(map.background(), true) + .set_position(&Rect::new((10, 10).into(), (64, 64).into())) + .enable(); + + let dmas = gba.dma.dma(); + + example_logo::display_logo(&mut map, &mut vram); + + let mut pos: Vector2D = (10, 10).into(); + let mut velocity: Vector2D = Vector2D::new(1.into(), 1.into()); + + let vblank = VBlank::get(); + + let circle: Box<[_]> = (1..64i32) + .map(|i| { + let y = 32 - i; + let x = (32 * 32 - y * y).isqrt(); + let x1 = 32 - x; + let x2 = 32 + x; + + ((x1 as u16) << 8) | (x2 as u16) + }) + .collect(); + + let mut circle_poses = vec![0; 160]; + + loop { + pos += velocity; + + if pos.x.floor() > WIDTH - 64 || pos.x.floor() < 0 { + velocity.x *= -1; + } + + if pos.y.floor() > HEIGHT - 64 || pos.y.floor() < 0 { + velocity.y *= -1; + } + + let x_pos = pos.x.floor().max(0) as u16; + let y_pos = pos.y.floor().max(0); + let x_adjustment = x_pos << 8 | x_pos; + for (i, value) in circle_poses.iter_mut().enumerate() { + let i = i as i32; + if i <= y_pos || i >= y_pos + 64 { + *value = 0; + continue; + } + + *value = circle[(i - y_pos) as usize - 1] + x_adjustment; + } + + window + .win_in(WinIn::Win0) + .set_position(&Rect::new(pos.floor(), (64, 65).into())); + window.commit(); + + let dma_controllable = window.win_in(WinIn::Win0).horizontal_position_dma(); + let _transfer = unsafe { dmas.dma0.hblank_transfer(&dma_controllable, &circle_poses) }; + + vblank.wait_for_vblank(); + } +} diff --git a/agb/src/display/window.rs b/agb/src/display/window.rs index 1901d193..488f4c22 100644 --- a/agb/src/display/window.rs +++ b/agb/src/display/window.rs @@ -2,7 +2,7 @@ //! The window feature of the GBA. use core::marker::PhantomData; -use crate::{fixnum::Rect, memory_mapped::MemoryMapped}; +use crate::{dma, fixnum::Rect, memory_mapped::MemoryMapped}; use super::{tiled::BackgroundID, DISPLAY_CONTROL, HEIGHT, WIDTH}; @@ -266,4 +266,14 @@ impl MovableWindow { ); self.set_position_u8(new_rect) } + + /// DMA to control the horizontal position of the window. The lower 8 bits are + /// the left hand side, and the upper 8 bits are the right hand side. + /// + /// When you use this, you should also set the height of the window approprately using + /// [`set_position`](Self::set_position). + #[must_use] + pub fn horizontal_position_dma(&self) -> dma::DmaControllable { + dma::DmaControllable::new(unsafe { REG_HORIZONTAL_BASE.add(self.id) }) + } }