diff --git a/agb/src/display/affine.rs b/agb/src/display/affine.rs index 38412619..fa1b269c 100644 --- a/agb/src/display/affine.rs +++ b/agb/src/display/affine.rs @@ -356,6 +356,15 @@ impl AffineMatrixObject { y: 0.into(), } } + + pub(crate) fn components(self) -> [u16; 4] { + [ + self.a.to_raw() as u16, + self.b.to_raw() as u16, + self.c.to_raw() as u16, + self.d.to_raw() as u16, + ] + } } impl From for AffineMatrix { diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 299c7734..621b52c7 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,3 +1,4 @@ +mod affine; mod managed; mod sprites; mod unmanaged; @@ -7,9 +8,12 @@ pub use sprites::{ TagMap, }; +pub use affine::AffineMatrix; pub use managed::{OAMManager, Object}; pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; +pub(crate) use affine::init_affine; + use super::DISPLAY_CONTROL; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs new file mode 100644 index 00000000..c54a44ff --- /dev/null +++ b/agb/src/display/object/affine.rs @@ -0,0 +1,115 @@ +use alloc::rc::Rc; + +use crate::{display::affine::AffineMatrixObject, sync::Static}; + +use super::OBJECT_ATTRIBUTE_MEMORY; + +#[derive(Debug)] +struct AffineMatrixLocation { + location: u16, +} + +#[derive(Debug, Clone)] +pub(crate) struct AffineMatrixVram(Rc); + +#[derive(Debug)] +pub struct AffineMatrix { + location: AffineMatrixVram, +} + +impl AffineMatrix { + pub fn new(affine_matrix: AffineMatrixObject) -> Option { + let mut matrix = AFFINE_MATRIX_DISTRIBUTOR.get_matrix()?; + matrix.write(affine_matrix); + + Some(matrix) + } + + pub(crate) fn vram(self) -> AffineMatrixVram { + self.location + } + + fn write(&mut self, affine_matrix: AffineMatrixObject) { + let components = affine_matrix.components(); + let location = self.location.0.location as usize; + for (idx, component) in components.iter().enumerate() { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16) + .add(location * 4 * idx + 3) + .write_volatile(*component); + } + } + } +} + +impl AffineMatrixVram { + pub fn location(&self) -> u16 { + self.0.location + } +} + +impl Drop for AffineMatrixLocation { + fn drop(&mut self) { + // safety: obtained via affine matrix distributor + unsafe { AFFINE_MATRIX_DISTRIBUTOR.return_matrix(self.location) } + } +} + +struct AffineMatrixDistributor { + tip: Static, +} + +static AFFINE_MATRIX_DISTRIBUTOR: AffineMatrixDistributor = AffineMatrixDistributor::new(); + +pub(crate) unsafe fn init_affine() { + AFFINE_MATRIX_DISTRIBUTOR.initialise_affine_matricies(); +} + +impl AffineMatrixDistributor { + const fn new() -> Self { + AffineMatrixDistributor { + tip: Static::new(u16::MAX), + } + } + + unsafe fn initialise_affine_matricies(&self) { + for i in 0..32 { + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 16 + 3); + + if i == 31 { + // none + ptr.write_volatile(u16::MAX); + } else { + ptr.write_volatile(i as u16 + 1); + } + } + + self.tip.write(0); + } + + fn location_of(affine_matrix_location: u16) -> *mut u16 { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(affine_matrix_location as usize * 16 + 3) + } + } + + fn get_matrix(&self) -> Option { + let location = self.tip.read(); + if location == u16::MAX { + return None; + } + + let next_tip = unsafe { Self::location_of(location).read_volatile() }; + + self.tip.write(next_tip); + + Some(AffineMatrix { + location: AffineMatrixVram(Rc::new(AffineMatrixLocation { location })), + }) + } + + unsafe fn return_matrix(&self, mat_id: u16) { + Self::location_of(mat_id).write_volatile(mat_id); + self.tip.write(mat_id); + } +} diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 0e7eebe0..1c87c935 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -1,15 +1,13 @@ -use core::{ - cell::{Cell, UnsafeCell}, - marker::PhantomData, -}; +use core::cell::{Cell, UnsafeCell}; use agb_fixnum::Vector2D; -use alloc::vec::Vec; use slotmap::{new_key_type, SlotMap}; use crate::display::Priority; -use super::{AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject}; +use super::{ + AffineMatrix, AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject, +}; new_key_type! {struct ObjectKey; } @@ -416,6 +414,13 @@ impl Object<'_> { self } + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_affine_matrix(affine_matrix) }; + + self + } + pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_sprite(sprite) }; diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index 4e4bb3a2..f32f45f8 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -6,7 +6,7 @@ use self::attributes::{ ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2, }; -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct Attributes { a0: ObjectAttribute0, a1s: ObjectAttribute1Standard, diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index e10161d3..0ea32529 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -4,7 +4,9 @@ use agb_fixnum::Vector2D; use alloc::vec::Vec; use crate::display::{ - object::{sprites::SpriteVram, OBJECT_ATTRIBUTE_MEMORY}, + object::{ + affine::AffineMatrixVram, sprites::SpriteVram, AffineMatrix, OBJECT_ATTRIBUTE_MEMORY, + }, Priority, }; @@ -39,10 +41,7 @@ impl OAMSlot<'_> { // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. let frame_data = unsafe { &mut *self.frame_data.get() }; - // SAFETY: This is called here and in set_sprite, neither of which call the other. - let sprite = unsafe { &mut *object.sprites.get() }; - - frame_data.this_frame_sprites.push(sprite.clone()); + frame_data.this_frame_sprites.push(object.sprite.clone()); frame_data.up_to = self.slot as i32; } @@ -116,10 +115,11 @@ impl UnmanagedOAM<'_> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UnmanagedObject { attributes: Attributes, - sprites: UnsafeCell, + sprite: SpriteVram, + affine_matrix: Option, } impl UnmanagedObject { @@ -131,7 +131,8 @@ impl UnmanagedObject { let mut sprite = Self { attributes: Attributes::default(), - sprites: UnsafeCell::new(sprite), + sprite, + affine_matrix: None, }; sprite.attributes.set_sprite(sprite_location, shape, size); @@ -140,6 +141,7 @@ impl UnmanagedObject { sprite } + #[must_use] pub fn is_visible(&self) -> bool { self.attributes.is_visible() } @@ -151,6 +153,11 @@ impl UnmanagedObject { } pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { + assert!( + self.affine_matrix.is_some(), + "affine matrix must be set before enabling affine matrix!" + ); + self.attributes.show_affine(affine_mode); self @@ -199,6 +206,15 @@ impl UnmanagedObject { self } + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + let vram = affine_matrix.vram(); + let location = vram.location(); + self.affine_matrix = Some(vram); + self.attributes.set_affine_matrix(location); + + self + } + fn set_sprite_attributes(&mut self, sprite: &SpriteVram) -> &mut Self { let size = sprite.size(); let (shape, size) = size.shape_size(); @@ -212,9 +228,7 @@ impl UnmanagedObject { pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { self.set_sprite_attributes(&sprite); - // SAFETY: This is called here and in OAMSlot set, neither of which call the other. - let sprites = unsafe { &mut *self.sprites.get() }; - *sprites = sprite; + self.sprite = sprite; self } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 90bd7166..741ae90f 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -236,6 +236,7 @@ impl Gba { #[doc(hidden)] #[must_use] pub unsafe fn new_in_entry() -> Self { + display::object::init_affine(); Self::single_new() }