mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 08:41:34 +11:00
sprite allocator
This commit is contained in:
parent
beb9abbb7e
commit
1849571344
56
agb/Cargo.lock
generated
56
agb/Cargo.lock
generated
|
@ -24,6 +24,7 @@ dependencies = [
|
|||
"agb_sound_converter",
|
||||
"bare-metal",
|
||||
"bitflags",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -64,6 +65,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -124,6 +136,26 @@ dependencies = [
|
|||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.4.0"
|
||||
|
@ -145,6 +177,12 @@ dependencies = [
|
|||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.119"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.1"
|
||||
|
@ -195,6 +233,12 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.5"
|
||||
|
@ -270,3 +314,15 @@ name = "unicode-xid"
|
|||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
|
|
@ -26,6 +26,7 @@ agb_sound_converter = { version = "0.1.0", path = "../agb-sound-converter" }
|
|||
agb_macros = { version = "0.1.0", path = "../agb-macros" }
|
||||
agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" }
|
||||
bare-metal = "1.0"
|
||||
hashbrown = "0.12.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "thumbv6m-none-eabi"
|
||||
|
|
|
@ -49,7 +49,7 @@ pub(crate) struct BlockAllocator {
|
|||
}
|
||||
|
||||
impl BlockAllocator {
|
||||
pub(super) const unsafe fn new(start: StartEnd) -> Self {
|
||||
pub const unsafe fn new(start: StartEnd) -> Self {
|
||||
Self {
|
||||
inner_allocator: BumpAllocator::new(start),
|
||||
state: Mutex::new(RefCell::new(BlockAllocatorState {
|
||||
|
|
|
@ -6,11 +6,9 @@ use super::SendNonNull;
|
|||
use crate::interrupt::free;
|
||||
use bare_metal::{CriticalSection, Mutex};
|
||||
|
||||
pub(crate) struct AddrFn(pub fn() -> usize);
|
||||
|
||||
pub(crate) struct StartEnd {
|
||||
pub start: AddrFn,
|
||||
pub end: AddrFn,
|
||||
pub start: fn() -> usize,
|
||||
pub end: fn() -> usize,
|
||||
}
|
||||
|
||||
pub(crate) struct BumpAllocator {
|
||||
|
@ -34,7 +32,7 @@ impl BumpAllocator {
|
|||
let ptr = if let Some(c) = *current_ptr {
|
||||
c.as_ptr() as usize
|
||||
} else {
|
||||
self.start_end.borrow(*cs).start.0()
|
||||
(self.start_end.borrow(*cs).start)()
|
||||
};
|
||||
|
||||
let alignment_bitmask = layout.align() - 1;
|
||||
|
@ -45,7 +43,7 @@ impl BumpAllocator {
|
|||
let resulting_ptr = ptr + amount_to_add;
|
||||
let new_current_ptr = resulting_ptr + layout.size();
|
||||
|
||||
if new_current_ptr as usize >= self.start_end.borrow(*cs).end.0() {
|
||||
if new_current_ptr as usize >= (self.start_end.borrow(*cs).end)() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ use core::alloc::Layout;
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
mod block_allocator;
|
||||
mod bump_allocator;
|
||||
pub(crate) mod block_allocator;
|
||||
pub(crate) mod bump_allocator;
|
||||
|
||||
use block_allocator::BlockAllocator;
|
||||
|
||||
use self::bump_allocator::{AddrFn, StartEnd};
|
||||
use self::bump_allocator::StartEnd;
|
||||
|
||||
struct SendNonNull<T>(NonNull<T>);
|
||||
unsafe impl<T> Send for SendNonNull<T> {}
|
||||
|
@ -37,8 +37,8 @@ const EWRAM_END: usize = 0x0204_0000;
|
|||
#[global_allocator]
|
||||
static GLOBAL_ALLOC: BlockAllocator = unsafe {
|
||||
BlockAllocator::new(StartEnd {
|
||||
start: AddrFn(get_data_end),
|
||||
end: AddrFn(|| EWRAM_END),
|
||||
start: get_data_end,
|
||||
end: || EWRAM_END,
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
use core::cell::RefCell;
|
||||
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
|
||||
use super::palette16::Palette16;
|
||||
use super::{palette16, Priority, DISPLAY_CONTROL};
|
||||
use crate::agb_alloc::block_allocator::BlockAllocator;
|
||||
use crate::agb_alloc::bump_allocator::StartEnd;
|
||||
use crate::bitarray::Bitarray;
|
||||
use crate::fixnum::Vector2D;
|
||||
use crate::memory_mapped::MemoryMapped1DArray;
|
||||
|
||||
type AffineLoan<'a> = crate::arena::Loan<'a, 32>;
|
||||
type AffineArena = crate::arena::Arena<32>;
|
||||
static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
|
||||
BlockAllocator::new(StartEnd {
|
||||
start: || 0x06010000,
|
||||
end: || 0x06010000 + 1024 * 8 * 4,
|
||||
})
|
||||
};
|
||||
|
||||
const OBJECT_ATTRIBUTE_MEMORY: MemoryMapped1DArray<u16, 512> =
|
||||
unsafe { MemoryMapped1DArray::new(0x0700_0000) };
|
||||
|
@ -15,523 +24,92 @@ const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
|
|||
const TILE_SPRITE: MemoryMapped1DArray<u32, { 1024 * 8 }> =
|
||||
unsafe { MemoryMapped1DArray::new(0x06010000) };
|
||||
|
||||
/// Handles distributing objects and matrices along with operations that effect all objects.
|
||||
/// You can create an instance of this using the Gba struct.
|
||||
///
|
||||
/// This handles distribution of sprites, ensuring that object ids are not reused and are
|
||||
/// returned to the pool once you're done handling them.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![no_std]
|
||||
/// # #![no_main]
|
||||
/// #
|
||||
/// # use agb::Gba;
|
||||
/// #
|
||||
/// # #[agb::entry]
|
||||
/// # fn main() -> ! {
|
||||
/// let mut gba = Gba::new();
|
||||
/// let mut object = gba.display.object.get();
|
||||
/// #
|
||||
/// # loop {}
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct ObjectControl {
|
||||
objects: RefCell<Bitarray<4>>,
|
||||
affines: AffineArena,
|
||||
pub struct Sprite {
|
||||
palette: &'static Palette16,
|
||||
data: &'static [u8],
|
||||
}
|
||||
|
||||
struct ObjectLoan<'a> {
|
||||
index: u8,
|
||||
objects: &'a RefCell<Bitarray<4>>,
|
||||
struct SpriteBorrow<'a> {
|
||||
id: SpriteId,
|
||||
controller: &'a RefCell<SpriteControllerInner>,
|
||||
}
|
||||
|
||||
/// The standard object, without rotation.
|
||||
///
|
||||
/// You should create this from an instance of ObjectControl created using the Gba struct. Note that
|
||||
/// no changes made to this will be visible until `commit()` is called. You should call `commit()` during
|
||||
/// vblank to ensure that you get no visual artifacts.
|
||||
///
|
||||
/// This struct implements a sort of builder pattern, allowing you to chain settings together.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![no_std]
|
||||
/// # #![no_main]
|
||||
/// #
|
||||
/// # use agb::Gba;
|
||||
/// use agb::display::object::Size;
|
||||
///
|
||||
/// # #[agb::entry]
|
||||
/// # fn main() -> ! {
|
||||
/// # let mut gba = Gba::new();
|
||||
/// let mut object = gba.display.object.get();
|
||||
///
|
||||
/// let mut my_new_object = object.get_object_standard();
|
||||
/// my_new_object.set_x(50)
|
||||
/// .set_y(50)
|
||||
/// .set_sprite_Size(Size::S8x8)
|
||||
/// .set_tile_id(7)
|
||||
/// .show();
|
||||
///
|
||||
/// // some time later in vblank
|
||||
/// my_new_object.commit();
|
||||
/// # loop {}
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct ObjectStandard<'a> {
|
||||
attributes: ObjectAttribute,
|
||||
loan: ObjectLoan<'a>,
|
||||
struct Storage {
|
||||
location: u16,
|
||||
count: u16,
|
||||
}
|
||||
|
||||
/// The affine object, with potential for using a transformation matrix to alter
|
||||
/// how the sprite is rendered to screen.
|
||||
pub struct ObjectAffine<'a> {
|
||||
attributes: ObjectAttribute,
|
||||
loan: ObjectLoan<'a>,
|
||||
aff_loan: Option<AffineLoan<'a>>,
|
||||
pub struct Object<'a> {
|
||||
sprite: SpriteBorrow<'a>,
|
||||
}
|
||||
|
||||
/// Refers to an affine matrix in the OAM. Includes both an index and the
|
||||
/// components of the affine matrix.
|
||||
pub struct AffineMatrix<'a> {
|
||||
pub attributes: AffineMatrixAttributes,
|
||||
loan: AffineLoan<'a>,
|
||||
struct SpriteControllerInner {
|
||||
palette: HashMap<PaletteId, Storage>,
|
||||
sprite: HashMap<SpriteId, Storage>,
|
||||
}
|
||||
|
||||
/// The components of the affine matrix. The components are fixed point 8:8.
|
||||
/// TODO is a type that can handle fixed point arithmetic.
|
||||
pub struct AffineMatrixAttributes {
|
||||
pub p_a: i16,
|
||||
pub p_b: i16,
|
||||
pub p_c: i16,
|
||||
pub p_d: i16,
|
||||
pub struct SpriteController {
|
||||
inner: RefCell<SpriteControllerInner>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Mode {
|
||||
Normal = 0,
|
||||
Affine = 1,
|
||||
Hidden = 2,
|
||||
AffineDouble = 3,
|
||||
}
|
||||
pub struct ObjectController {}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Size {
|
||||
// stored as attr0 attr1
|
||||
S8x8 = 0b00_00,
|
||||
S16x16 = 0b00_01,
|
||||
S32x32 = 0b00_10,
|
||||
S64x64 = 0b00_11,
|
||||
/// The Sprite Id is a thin wrapper around the pointer to the sprite in
|
||||
/// rom and is therefore a unique identifier to a sprite
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct SpriteId(usize);
|
||||
|
||||
S16x8 = 0b01_00,
|
||||
S32x8 = 0b01_01,
|
||||
S32x16 = 0b01_10,
|
||||
S64x32 = 0b01_11,
|
||||
/// The palette id is a thin wrapper around the pointer to the palette in rom
|
||||
/// and is therefore a unique reference to a palette
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct PaletteId(usize);
|
||||
|
||||
S8x16 = 0b10_00,
|
||||
S8x32 = 0b10_01,
|
||||
S16x32 = 0b10_10,
|
||||
S32x64 = 0b10_11,
|
||||
}
|
||||
|
||||
impl ObjectStandard<'_> {
|
||||
/// Commits the object to OAM such that the updated version is displayed on
|
||||
/// screen. Recommend to do this during VBlank.
|
||||
pub fn commit(&self) {
|
||||
unsafe { self.attributes.commit(self.loan.index) }
|
||||
}
|
||||
|
||||
/// Sets the x coordinate of the sprite on screen.
|
||||
pub fn set_x(&mut self, x: u16) -> &mut Self {
|
||||
self.attributes.set_x(x);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the y coordinate of the sprite on screen.
|
||||
pub fn set_y(&mut self, y: u16) -> &mut Self {
|
||||
self.attributes.set_y(y);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the index of the tile to use as the sprite. Potentially a temporary function.
|
||||
pub fn set_tile_id(&mut self, id: u16) -> &mut Self {
|
||||
self.attributes.set_tile_id(id);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the sprite is horizontally mirrored or not.
|
||||
pub fn set_hflip(&mut self, hflip: bool) -> &mut Self {
|
||||
self.attributes.set_hflip(hflip);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the sprite size, will read tiles in x major order to construct this.
|
||||
pub fn set_sprite_size(&mut self, size: Size) -> &mut Self {
|
||||
self.attributes.set_size(size);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Show the object on screen.
|
||||
pub fn show(&mut self) -> &mut Self {
|
||||
self.attributes.set_mode(Mode::Normal);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Hide the object and do not render.
|
||||
pub fn hide(&mut self) -> &mut Self {
|
||||
self.attributes.set_mode(Mode::Hidden);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the palette to use for this sprite
|
||||
pub fn set_palette(&mut self, palette: u16) -> &mut Self {
|
||||
self.attributes.set_palette(palette);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the x and y position of the object, performing casts as nessesary
|
||||
/// to fit within the bits allocated for this purpose.
|
||||
pub fn set_position(&mut self, position: Vector2D<i32>) -> &mut Self {
|
||||
let x = position.x as u16;
|
||||
let y = position.y as u16;
|
||||
self.attributes.set_x(x);
|
||||
self.attributes.set_y(y);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the priority (used for z ordering) of this sprite
|
||||
pub fn set_priority(&mut self, p: Priority) -> &mut Self {
|
||||
self.attributes.set_priority(p);
|
||||
|
||||
self
|
||||
impl Sprite {
|
||||
fn get_id(&'static self) -> SpriteId {
|
||||
SpriteId(self as *const _ as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ObjectAffine<'a> {
|
||||
/// Commits the object to OAM such that the updated version is displayed on
|
||||
/// screen. Recommend to do this during VBlank.
|
||||
pub fn commit(&self) {
|
||||
unsafe { self.attributes.commit(self.loan.index) }
|
||||
impl SpriteController {
|
||||
fn get_sprite(&self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
|
||||
let inner = self.inner.borrow_mut();
|
||||
let id = sprite.get_id();
|
||||
if let Some(storage) = inner.sprite.get_mut(&id) {
|
||||
storage.count += 1;
|
||||
Some(SpriteBorrow {
|
||||
id,
|
||||
controller: &self.inner,
|
||||
})
|
||||
} else {
|
||||
// allocate a new sprite
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Sets the x coordinate of the sprite on screen.
|
||||
pub fn set_x(&mut self, x: u16) {
|
||||
self.attributes.set_x(x)
|
||||
}
|
||||
/// Sets the y coordinate of the sprite on screen.
|
||||
pub fn set_y(&mut self, y: u16) {
|
||||
self.attributes.set_y(y)
|
||||
}
|
||||
/// Sets the index of the tile to use as the sprite. Potentially a temporary function.
|
||||
pub fn set_tile_id(&mut self, id: u16) {
|
||||
self.attributes.set_tile_id(id)
|
||||
}
|
||||
/// Sets the sprite size, will read tiles in x major order to construct this.
|
||||
pub fn set_sprite_size(&mut self, size: Size) {
|
||||
self.attributes.set_size(size);
|
||||
}
|
||||
|
||||
/// Show the object on screen. Panics if affine matrix has not been set.
|
||||
pub fn show(&mut self) {
|
||||
if self.aff_loan.is_none() {
|
||||
panic!("affine matrix should be set")
|
||||
}
|
||||
self.attributes.set_mode(Mode::Affine)
|
||||
}
|
||||
/// Hide the object and do not render the sprite.
|
||||
pub fn hide(&mut self) {
|
||||
self.attributes.set_mode(Mode::Hidden)
|
||||
}
|
||||
|
||||
/// Sets the affine matrix to use. Changing the affine matrix will change
|
||||
/// how the sprite is rendered.
|
||||
pub fn set_affine_mat(&mut self, aff: &AffineMatrix<'a>) {
|
||||
self.attributes.set_affine(aff.loan.my_index);
|
||||
self.aff_loan = Some(aff.loan.clone());
|
||||
}
|
||||
|
||||
/// Sets the x and y position of the object, performing casts as nessesary
|
||||
/// to fit within the bits allocated for this purpose.
|
||||
pub fn set_position(&mut self, position: Vector2D<i32>) {
|
||||
let x = position.x as u16;
|
||||
let y = position.y as u16;
|
||||
self.attributes.set_x(x);
|
||||
self.attributes.set_y(y);
|
||||
}
|
||||
|
||||
pub fn set_priority(&mut self, p: Priority) {
|
||||
self.attributes.set_priority(p)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_bits(current: u16, value: u16, length: u16, shift: u16) -> u16 {
|
||||
let mask: u16 = (1 << length) - 1;
|
||||
(current & !(mask << shift)) | ((value & mask) << shift)
|
||||
}
|
||||
|
||||
impl Drop for ObjectLoan<'_> {
|
||||
impl<'a> Drop for SpriteBorrow<'a> {
|
||||
fn drop(&mut self) {
|
||||
let attributes = ObjectAttribute::new();
|
||||
unsafe {
|
||||
attributes.commit(self.index);
|
||||
let inner = self.controller.borrow_mut();
|
||||
let entry = inner
|
||||
.sprite
|
||||
.entry(self.id)
|
||||
.and_replace_entry_with(|_, mut storage| {
|
||||
storage.count -= 1;
|
||||
if storage.count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(storage)
|
||||
}
|
||||
let mut objs = self.objects.borrow_mut();
|
||||
objs.set(self.index as usize, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
struct ObjectAttribute {
|
||||
a0: u16,
|
||||
a1: u16,
|
||||
a2: u16,
|
||||
}
|
||||
|
||||
impl ObjectAttribute {
|
||||
unsafe fn commit(&self, index: u8) {
|
||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4, self.a0);
|
||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 1, self.a1);
|
||||
OBJECT_ATTRIBUTE_MEMORY.set(index as usize * 4 + 2, self.a2);
|
||||
match entry {
|
||||
Entry::Vacant(_) => {
|
||||
// free the underlying resource.
|
||||
// palette might be unused too.
|
||||
}
|
||||
|
||||
fn set_hflip(&mut self, hflip: bool) {
|
||||
self.a1 = set_bits(self.a1, hflip as u16, 1, 0xC);
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: Size) {
|
||||
let a1 = size as u16 & 0b11;
|
||||
let a0 = (size as u16 >> 2) & 0b11;
|
||||
|
||||
self.a0 = set_bits(self.a0, a0, 2, 0xE);
|
||||
self.a1 = set_bits(self.a1, a1, 2, 0xE);
|
||||
}
|
||||
|
||||
fn set_palette(&mut self, palette: u16) {
|
||||
self.a2 = set_bits(self.a2, palette, 4, 0xC);
|
||||
}
|
||||
|
||||
fn set_x(&mut self, x: u16) {
|
||||
self.a1 = set_bits(self.a1, x, 9, 0);
|
||||
}
|
||||
|
||||
fn set_y(&mut self, y: u16) {
|
||||
self.a0 = set_bits(self.a0, y, 8, 0)
|
||||
}
|
||||
|
||||
fn set_tile_id(&mut self, id: u16) {
|
||||
self.a2 = set_bits(self.a2, id, 10, 0);
|
||||
}
|
||||
|
||||
fn set_mode(&mut self, mode: Mode) {
|
||||
self.a0 = set_bits(self.a0, mode as u16, 2, 8);
|
||||
}
|
||||
|
||||
fn set_affine(&mut self, aff_id: u8) {
|
||||
self.a1 = set_bits(self.a1, aff_id as u16, 5, 0x9);
|
||||
}
|
||||
|
||||
fn set_priority(&mut self, p: Priority) {
|
||||
self.a2 = set_bits(self.a2, p as u16, 2, 0x0A);
|
||||
}
|
||||
}
|
||||
|
||||
impl AffineMatrix<'_> {
|
||||
/// Commits matrix to OAM, will cause any objects using this matrix to be updated.
|
||||
pub fn commit(&self) {
|
||||
unsafe { self.attributes.commit(self.loan.my_index) };
|
||||
}
|
||||
}
|
||||
|
||||
impl AffineMatrixAttributes {
|
||||
#[allow(clippy::identity_op)]
|
||||
unsafe fn commit(&self, index: u8) {
|
||||
let index = index as usize * 4;
|
||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 0) * 4 + 3, self.p_a as u16);
|
||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 1) * 4 + 3, self.p_b as u16);
|
||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 2) * 4 + 3, self.p_c as u16);
|
||||
OBJECT_ATTRIBUTE_MEMORY.set((index + 3) * 4 + 3, self.p_d as u16);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectAttribute {
|
||||
fn new() -> Self {
|
||||
let mut o = ObjectAttribute {
|
||||
a0: 0,
|
||||
a1: 0,
|
||||
a2: 0,
|
||||
};
|
||||
o.set_mode(Mode::Hidden);
|
||||
o
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectControl {
|
||||
pub(crate) fn new() -> Self {
|
||||
let o = ObjectAttribute::new();
|
||||
for index in 0..128 {
|
||||
unsafe { o.commit(index) };
|
||||
}
|
||||
ObjectControl {
|
||||
objects: RefCell::new(Bitarray::new()),
|
||||
affines: AffineArena::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_sprite_tilemap_entry(&self, index: usize, data: u32) {
|
||||
TILE_SPRITE.set(index, data);
|
||||
}
|
||||
|
||||
/// Copies raw palettes to the background palette without any checks.
|
||||
pub fn set_sprite_palette_raw(&self, colour: &[u16]) {
|
||||
for (index, &entry) in colour.iter().enumerate() {
|
||||
self.set_sprite_palette_entry(index, entry)
|
||||
}
|
||||
}
|
||||
fn set_sprite_palette_entry(&self, index: usize, colour: u16) {
|
||||
PALETTE_SPRITE.set(index, colour)
|
||||
}
|
||||
|
||||
fn set_sprite_palette(&self, pal_index: u8, palette: &palette16::Palette16) {
|
||||
for (colour_index, &colour) in palette.colours.iter().enumerate() {
|
||||
PALETTE_SPRITE.set(pal_index as usize * 16 + colour_index, colour);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sprite_palettes(&self, palettes: &[palette16::Palette16]) {
|
||||
for (palette_index, entry) in palettes.iter().enumerate() {
|
||||
self.set_sprite_palette(palette_index as u8, entry)
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies tiles to the sprite tilemap without any checks.
|
||||
pub fn set_sprite_tilemap(&self, tiles: &[u32]) {
|
||||
for (index, &tile) in tiles.iter().enumerate() {
|
||||
self.set_sprite_tilemap_entry(index, tile)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_sprite_tilemap_at_idx(&self, idx: usize, tiles: &[u32]) {
|
||||
for (index, &tile) in tiles.iter().enumerate() {
|
||||
self.set_sprite_tilemap_entry(index + idx, tile)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable objects on the GBA.
|
||||
pub fn enable(&mut self) {
|
||||
let disp = DISPLAY_CONTROL.get();
|
||||
let disp = disp | (1 << 0x0C);
|
||||
DISPLAY_CONTROL.set(disp);
|
||||
}
|
||||
|
||||
/// Disable objects, objects won't be rendered.
|
||||
pub fn disable(&mut self) {
|
||||
let disp = DISPLAY_CONTROL.get();
|
||||
let disp = disp & !(1 << 0x0C);
|
||||
DISPLAY_CONTROL.set(disp);
|
||||
}
|
||||
|
||||
fn get_unused_object_index(&self) -> u8 {
|
||||
let mut objects = self.objects.borrow_mut();
|
||||
for index in 0..128 {
|
||||
if !objects.get(index).unwrap() {
|
||||
objects.set(index, true);
|
||||
return index as u8;
|
||||
}
|
||||
}
|
||||
panic!("object id must be less than 128");
|
||||
}
|
||||
|
||||
/// Get an unused standard object. Panics if more than 128 objects are
|
||||
/// obtained.
|
||||
pub fn get_object_standard(&self) -> ObjectStandard {
|
||||
let id = self.get_unused_object_index();
|
||||
ObjectStandard {
|
||||
attributes: ObjectAttribute::new(),
|
||||
loan: ObjectLoan {
|
||||
objects: &self.objects,
|
||||
index: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an unused affine object. Panics if more than 128 objects are
|
||||
/// obtained.
|
||||
pub fn get_object_affine(&self) -> ObjectAffine {
|
||||
let id = self.get_unused_object_index();
|
||||
ObjectAffine {
|
||||
attributes: ObjectAttribute::new(),
|
||||
loan: ObjectLoan {
|
||||
objects: &self.objects,
|
||||
index: id,
|
||||
},
|
||||
aff_loan: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an unused affine matrix. Panics if more than 32 affine matricies are
|
||||
/// obtained.
|
||||
pub fn get_affine(&self) -> AffineMatrix {
|
||||
AffineMatrix {
|
||||
attributes: AffineMatrixAttributes {
|
||||
p_a: 0,
|
||||
p_b: 0,
|
||||
p_c: 0,
|
||||
p_d: 0,
|
||||
},
|
||||
loan: self
|
||||
.affines
|
||||
.get_next_free()
|
||||
.expect("there are no affines avaliable"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test_case]
|
||||
fn get_and_release_object(gba: &mut crate::Gba) {
|
||||
let objs = gba.display.object.get();
|
||||
|
||||
let _o1 = {
|
||||
let o0 = objs.get_object_standard();
|
||||
let o1 = objs.get_object_standard();
|
||||
assert_eq!(o0.loan.index, 0);
|
||||
assert_eq!(o1.loan.index, 1);
|
||||
o1
|
||||
};
|
||||
|
||||
let o0 = objs.get_object_standard();
|
||||
assert_eq!(o0.loan.index, 0);
|
||||
let o2 = objs.get_object_affine();
|
||||
assert_eq!(o2.loan.index, 2);
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn get_and_release_affine(gba: &mut crate::Gba) {
|
||||
let objs = gba.display.object.get();
|
||||
|
||||
let _a1 = {
|
||||
let a0 = objs.get_affine();
|
||||
let a1 = objs.get_affine();
|
||||
assert_eq!(a0.loan.my_index, 0);
|
||||
assert_eq!(a1.loan.my_index, 1);
|
||||
a1
|
||||
};
|
||||
|
||||
let a0 = objs.get_affine();
|
||||
assert_eq!(a0.loan.my_index, 0);
|
||||
let a2 = objs.get_affine();
|
||||
assert_eq!(a2.loan.my_index, 2);
|
||||
}
|
||||
}
|
||||
impl ObjectController {}
|
||||
|
|
Loading…
Reference in a new issue