mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 08:41:34 +11:00
affines are referenced counted with the new RC arena(ish) implementation
This commit is contained in:
parent
e92383e29c
commit
8d0c50b37a
140
agb/src/arena.rs
Normal file
140
agb/src/arena.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
use core::{cell::RefCell, num::NonZeroU8};
|
||||||
|
|
||||||
|
type Index = u8;
|
||||||
|
|
||||||
|
pub struct Loan<'a, const S: usize> {
|
||||||
|
pub my_index: Index,
|
||||||
|
arena: &'a RefCell<ArenaInner<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Next should be an option, however this raises this struct to 3 bytes where
|
||||||
|
/// it is now only 2 bytes This saves a byte per entry and therefore it is
|
||||||
|
/// probably worth it.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Element {
|
||||||
|
Contains(NonZeroU8),
|
||||||
|
Next(Index),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ArenaInner<const S: usize> {
|
||||||
|
arena: [Element; S],
|
||||||
|
first: Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Arena<const S: usize> {
|
||||||
|
arena_inner: RefCell<ArenaInner<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const S: usize> Arena<S> {
|
||||||
|
pub fn new() -> Arena<S> {
|
||||||
|
// we use the special value u8::MAX as a None
|
||||||
|
assert!(S < u8::MAX as usize - 1);
|
||||||
|
|
||||||
|
let mut arena: [Element; S] = [Element::Next(u8::MAX); S];
|
||||||
|
arena
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(index, e)| *e = Element::Next(index as Index + 1));
|
||||||
|
arena.last_mut().map(|a| *a = Element::Next(u8::MAX));
|
||||||
|
Arena {
|
||||||
|
arena_inner: RefCell::new(ArenaInner { arena, first: 0 }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_next_free(&self) -> Option<Loan<S>> {
|
||||||
|
let mut arena = self.arena_inner.borrow_mut();
|
||||||
|
let i = arena.first;
|
||||||
|
if i == u8::MAX {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Element::Next(n) = arena.arena[i as usize] {
|
||||||
|
arena.first = n;
|
||||||
|
} else {
|
||||||
|
unreachable!("invalid state, next points to already occupied state");
|
||||||
|
}
|
||||||
|
arena.arena[i as usize] = Element::Contains(NonZeroU8::new(1).unwrap());
|
||||||
|
Some(Loan {
|
||||||
|
my_index: i,
|
||||||
|
arena: &self.arena_inner,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const S: usize> Drop for Loan<'_, S> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut arena = self.arena.borrow_mut();
|
||||||
|
let me = &mut arena.arena[self.my_index as usize];
|
||||||
|
match me {
|
||||||
|
Element::Contains(n) => {
|
||||||
|
let mut a = n.get();
|
||||||
|
a -= 1;
|
||||||
|
if a == 0 {
|
||||||
|
arena.arena[self.my_index as usize] = Element::Next(arena.first);
|
||||||
|
arena.first = self.my_index;
|
||||||
|
} else {
|
||||||
|
*n = NonZeroU8::new(a).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("if a loan exists the correspoinding arena entry should be filled"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const S: usize> Clone for Loan<'_, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match &mut self.arena.borrow_mut().arena[self.my_index as usize] {
|
||||||
|
Element::Contains(n) => {
|
||||||
|
let a = n.get();
|
||||||
|
*n = NonZeroU8::new(a + 1).unwrap();
|
||||||
|
}
|
||||||
|
_ => unreachable!("if a loan exists the correspoinding arena entry should be filled"),
|
||||||
|
}
|
||||||
|
Loan {
|
||||||
|
my_index: self.my_index,
|
||||||
|
arena: self.arena,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use alloc;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn size_of_element(_gba: &mut crate::Gba) {
|
||||||
|
let s = core::mem::size_of::<Element>();
|
||||||
|
assert_eq!(s, 2, "elements should be of a minimum size");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn get_everything(_gba: &mut crate::Gba) {
|
||||||
|
let s: Arena<4> = Arena::new();
|
||||||
|
{
|
||||||
|
let mut c = alloc::vec::Vec::new();
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.iter().for_each(|a| assert!(a.is_some()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut c = alloc::vec::Vec::new();
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.iter().for_each(|a| assert!(a.is_some()));
|
||||||
|
assert!(s.get_next_free().is_none());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut c = alloc::vec::Vec::new();
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.push(s.get_next_free());
|
||||||
|
c.iter().for_each(|a| assert!(a.is_some()));
|
||||||
|
assert!(s.get_next_free().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,9 @@ use crate::bitarray::Bitarray;
|
||||||
use crate::memory_mapped::MemoryMapped1DArray;
|
use crate::memory_mapped::MemoryMapped1DArray;
|
||||||
use crate::number::Vector2D;
|
use crate::number::Vector2D;
|
||||||
|
|
||||||
|
type AffineLoan<'a> = crate::arena::Loan<'a, 32>;
|
||||||
|
type AffineArena = crate::arena::Arena<32>;
|
||||||
|
|
||||||
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
|
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
|
||||||
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
|
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
|
||||||
const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
|
const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
|
||||||
|
@ -15,7 +18,7 @@ const TILE_SPRITE: MemoryMapped1DArray<u32, { 512 * 8 }> =
|
||||||
/// Handles distributing objects and matricies along with operations that effect all objects.
|
/// Handles distributing objects and matricies along with operations that effect all objects.
|
||||||
pub struct ObjectControl {
|
pub struct ObjectControl {
|
||||||
objects: RefCell<Bitarray<4>>,
|
objects: RefCell<Bitarray<4>>,
|
||||||
affines: RefCell<Bitarray<1>>,
|
affines: AffineArena,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ObjectLoan<'a> {
|
struct ObjectLoan<'a> {
|
||||||
|
@ -23,11 +26,6 @@ struct ObjectLoan<'a> {
|
||||||
objects: &'a RefCell<Bitarray<4>>,
|
objects: &'a RefCell<Bitarray<4>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AffineLoan<'a> {
|
|
||||||
index: u8,
|
|
||||||
affines: &'a RefCell<Bitarray<1>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The standard object, without rotation.
|
/// The standard object, without rotation.
|
||||||
pub struct ObjectStandard<'a> {
|
pub struct ObjectStandard<'a> {
|
||||||
attributes: ObjectAttribute,
|
attributes: ObjectAttribute,
|
||||||
|
@ -39,7 +37,7 @@ pub struct ObjectStandard<'a> {
|
||||||
pub struct ObjectAffine<'a> {
|
pub struct ObjectAffine<'a> {
|
||||||
attributes: ObjectAttribute,
|
attributes: ObjectAttribute,
|
||||||
loan: ObjectLoan<'a>,
|
loan: ObjectLoan<'a>,
|
||||||
aff_id: Option<u8>,
|
aff_loan: Option<AffineLoan<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refers to an affine matrix in the OAM. Includes both an index and the
|
/// Refers to an affine matrix in the OAM. Includes both an index and the
|
||||||
|
@ -161,7 +159,7 @@ impl<'a> ObjectAffine<'a> {
|
||||||
|
|
||||||
/// Show the object on screen. Panics if affine matrix has not been set.
|
/// Show the object on screen. Panics if affine matrix has not been set.
|
||||||
pub fn show(&mut self) {
|
pub fn show(&mut self) {
|
||||||
if self.aff_id.is_none() {
|
if self.aff_loan.is_none() {
|
||||||
panic!("affine matrix should be set")
|
panic!("affine matrix should be set")
|
||||||
}
|
}
|
||||||
self.attributes.set_mode(Mode::Affine)
|
self.attributes.set_mode(Mode::Affine)
|
||||||
|
@ -170,11 +168,12 @@ impl<'a> ObjectAffine<'a> {
|
||||||
pub fn hide(&mut self) {
|
pub fn hide(&mut self) {
|
||||||
self.attributes.set_mode(Mode::Hidden)
|
self.attributes.set_mode(Mode::Hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the affine matrix to use. Changing the affine matrix will change
|
/// Sets the affine matrix to use. Changing the affine matrix will change
|
||||||
/// how the sprite is rendered.
|
/// how the sprite is rendered.
|
||||||
pub fn set_affine_mat(&mut self, aff: &AffineMatrix) {
|
pub fn set_affine_mat(&mut self, aff: &AffineMatrix<'a>) {
|
||||||
self.attributes.set_affine(aff.loan.index);
|
self.attributes.set_affine(aff.loan.my_index);
|
||||||
self.aff_id = Some(aff.loan.index);
|
self.aff_loan = Some(aff.loan.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the x and y position of the object, performing casts as nessesary
|
/// Sets the x and y position of the object, performing casts as nessesary
|
||||||
|
@ -207,22 +206,6 @@ impl Drop for ObjectLoan<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AffineLoan<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let attributes = AffineMatrixAttributes {
|
|
||||||
p_a: 0,
|
|
||||||
p_b: 0,
|
|
||||||
p_c: 0,
|
|
||||||
p_d: 0,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
attributes.commit(self.index);
|
|
||||||
}
|
|
||||||
let mut affs = self.affines.borrow_mut();
|
|
||||||
affs.set(self.index as usize, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ObjectAttribute {
|
struct ObjectAttribute {
|
||||||
a0: u16,
|
a0: u16,
|
||||||
a1: u16,
|
a1: u16,
|
||||||
|
@ -276,7 +259,7 @@ impl ObjectAttribute {
|
||||||
impl AffineMatrix<'_> {
|
impl AffineMatrix<'_> {
|
||||||
/// Commits matrix to OAM, will cause any objects using this matrix to be updated.
|
/// Commits matrix to OAM, will cause any objects using this matrix to be updated.
|
||||||
pub fn commit(&self) {
|
pub fn commit(&self) {
|
||||||
unsafe { self.attributes.commit(self.loan.index) };
|
unsafe { self.attributes.commit(self.loan.my_index) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +294,7 @@ impl ObjectControl {
|
||||||
}
|
}
|
||||||
ObjectControl {
|
ObjectControl {
|
||||||
objects: RefCell::new(Bitarray::new()),
|
objects: RefCell::new(Bitarray::new()),
|
||||||
affines: RefCell::new(Bitarray::new()),
|
affines: AffineArena::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,17 +356,6 @@ impl ObjectControl {
|
||||||
panic!("object id must be less than 128");
|
panic!("object id must be less than 128");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_unused_affine_index(&self) -> u8 {
|
|
||||||
let mut affines = self.affines.borrow_mut();
|
|
||||||
for index in 0..32 {
|
|
||||||
if !affines.get(index).unwrap() {
|
|
||||||
affines.set(index, true);
|
|
||||||
return index as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic!("affine id must be less than 32");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an unused standard object. Panics if more than 128 objects are
|
/// Get an unused standard object. Panics if more than 128 objects are
|
||||||
/// obtained.
|
/// obtained.
|
||||||
pub fn get_object_standard(&self) -> ObjectStandard {
|
pub fn get_object_standard(&self) -> ObjectStandard {
|
||||||
|
@ -407,14 +379,13 @@ impl ObjectControl {
|
||||||
objects: &self.objects,
|
objects: &self.objects,
|
||||||
index: id,
|
index: id,
|
||||||
},
|
},
|
||||||
aff_id: None,
|
aff_loan: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an unused affine matrix. Panics if more than 32 affine matricies are
|
/// Get an unused affine matrix. Panics if more than 32 affine matricies are
|
||||||
/// obtained.
|
/// obtained.
|
||||||
pub fn get_affine(&self) -> AffineMatrix {
|
pub fn get_affine(&self) -> AffineMatrix {
|
||||||
let id = self.get_unused_affine_index();
|
|
||||||
AffineMatrix {
|
AffineMatrix {
|
||||||
attributes: AffineMatrixAttributes {
|
attributes: AffineMatrixAttributes {
|
||||||
p_a: 0,
|
p_a: 0,
|
||||||
|
@ -422,10 +393,10 @@ impl ObjectControl {
|
||||||
p_c: 0,
|
p_c: 0,
|
||||||
p_d: 0,
|
p_d: 0,
|
||||||
},
|
},
|
||||||
loan: AffineLoan {
|
loan: self
|
||||||
affines: &self.affines,
|
.affines
|
||||||
index: id,
|
.get_next_free()
|
||||||
},
|
.expect("there are no affines avaliable"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,14 +428,14 @@ mod tests {
|
||||||
let _a1 = {
|
let _a1 = {
|
||||||
let a0 = objs.get_affine();
|
let a0 = objs.get_affine();
|
||||||
let a1 = objs.get_affine();
|
let a1 = objs.get_affine();
|
||||||
assert_eq!(a0.loan.index, 0);
|
assert_eq!(a0.loan.my_index, 0);
|
||||||
assert_eq!(a1.loan.index, 1);
|
assert_eq!(a1.loan.my_index, 1);
|
||||||
a1
|
a1
|
||||||
};
|
};
|
||||||
|
|
||||||
let a0 = objs.get_affine();
|
let a0 = objs.get_affine();
|
||||||
assert_eq!(a0.loan.index, 0);
|
assert_eq!(a0.loan.my_index, 0);
|
||||||
let a2 = objs.get_affine();
|
let a2 = objs.get_affine();
|
||||||
assert_eq!(a2.loan.index, 2);
|
assert_eq!(a2.loan.my_index, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,31 +13,29 @@
|
||||||
//! internal workings of the Game Boy Advance whilst still being high
|
//! internal workings of the Game Boy Advance whilst still being high
|
||||||
//! performance and memory efficient.
|
//! performance and memory efficient.
|
||||||
|
|
||||||
|
pub use agb_image_converter::include_gfx;
|
||||||
|
pub use agb_macros::entry;
|
||||||
|
pub use agb_sound_converter::include_wav;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
mod agb_alloc;
|
mod agb_alloc;
|
||||||
|
|
||||||
|
mod arena;
|
||||||
|
mod bitarray;
|
||||||
/// Implements everything relating to things that are displayed on screen.
|
/// Implements everything relating to things that are displayed on screen.
|
||||||
pub mod display;
|
pub mod display;
|
||||||
/// Button inputs to the system.
|
/// Button inputs to the system.
|
||||||
pub mod input;
|
pub mod input;
|
||||||
/// Implements sound output.
|
|
||||||
pub mod sound;
|
|
||||||
|
|
||||||
pub use agb_image_converter::include_gfx;
|
|
||||||
pub use agb_sound_converter::include_wav;
|
|
||||||
|
|
||||||
pub use agb_macros::entry;
|
|
||||||
|
|
||||||
mod bitarray;
|
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
mod memory_mapped;
|
mod memory_mapped;
|
||||||
/// Implements logging to the mgba emulator.
|
/// Implements logging to the mgba emulator.
|
||||||
pub mod mgba;
|
pub mod mgba;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
mod single;
|
mod single;
|
||||||
|
/// Implements sound output.
|
||||||
|
pub mod sound;
|
||||||
/// System BIOS calls / syscalls.
|
/// System BIOS calls / syscalls.
|
||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue