From 2a7b350f57acd2b18c60cac398bd198e56da96e8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 12 Feb 2022 23:57:10 +0000 Subject: [PATCH] objects now exist and work --- agb/Cargo.lock | 28 +++++ agb/Cargo.toml | 1 + agb/examples/chicken.rs | 117 +++++++++++++++----- agb/src/display/mod.rs | 8 +- agb/src/display/object.rs | 219 ++++++++++++++++++++++++++++++++++---- agb/src/syscall.rs | 92 ++++++++-------- 6 files changed, 370 insertions(+), 95 deletions(-) diff --git a/agb/Cargo.lock b/agb/Cargo.lock index cf0c71a0..bd85fdac 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "bare-metal", "bitflags", "hashbrown", + "modular-bitfield", ] [[package]] @@ -192,6 +193,27 @@ dependencies = [ "adler", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -289,6 +311,12 @@ dependencies = [ "syn", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.86" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 0dcfbade..ddbb7945 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -27,6 +27,7 @@ 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" +modular-bitfield = "0.11.2" [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 359a4811..9113ea56 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -2,7 +2,12 @@ #![no_main] use agb::{ - display::{background::Map, object::ObjectStandard, HEIGHT, WIDTH}, + display::{ + background::Map, + object::{Object, ObjectController, Size, Sprite}, + palette16::Palette16, + HEIGHT, WIDTH, + }, input::Button, }; use core::convert::TryInto; @@ -15,7 +20,7 @@ enum State { } struct Character<'a> { - object: ObjectStandard<'a>, + object: Object<'a, 'a>, position: Vector2D, velocity: Vector2D, } @@ -30,8 +35,8 @@ fn tile_is_collidable(tile: u16) -> bool { masked == 0 || masked == 4 } -fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 { - (((count / delay) % (end + 1 - start)) + start) as u16 +fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> usize { + (((count / delay) % (end + 1 - start)) + start) as usize } #[agb::entry] @@ -54,14 +59,11 @@ fn main(mut gba: agb::Gba) -> ! { background.show(); background.commit(); - let mut object = gba.display.object.get(); + let object = gba.display.object.get(); - object.set_sprite_palette_raw(&CHICKEN_PALETTE); - object.set_sprite_tilemap(&CHICKEN_TILES); - - object.enable(); + let sprite = object.get_sprite(&ChickenSprites[0]).unwrap(); let mut chicken = Character { - object: object.get_object_standard(), + object: object.get_object(sprite).unwrap(), position: Vector2D { x: (6 * 8) << 8, y: ((7 * 8) - 4) << 8, @@ -69,7 +71,6 @@ fn main(mut gba: agb::Gba) -> ! { velocity: Vector2D { x: 0, y: 0 }, }; - chicken.object.set_tile_id(0); chicken .object .set_x((chicken.position.x >> 8).try_into().unwrap()); @@ -120,14 +121,19 @@ fn main(mut gba: agb::Gba) -> ! { } restrict_to_screen(&mut chicken); - update_chicken_object(&mut chicken, state, frame_count); + update_chicken_object(&mut chicken, &object, state, frame_count); // Commit the chicken to vram chicken.object.commit(); } } -fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32) { +fn update_chicken_object<'a>( + chicken: &'_ mut Character<'a>, + object: &'a ObjectController, + state: State, + frame_count: u32, +) { if chicken.velocity.x > 1 { chicken.object.set_hflip(false); } else if chicken.velocity.x < -1 { @@ -136,18 +142,24 @@ fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32 match state { State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { + chicken.object.set_sprite( + object + .get_sprite(&ChickenSprites[frame_ranger(frame_count, 1, 3, 10)]) + .unwrap(), + ); + } else { chicken .object - .set_tile_id(frame_ranger(frame_count, 1, 3, 10)); - } else { - chicken.object.set_tile_id(0); + .set_sprite(object.get_sprite(&ChickenSprites[0]).unwrap()); } } State::Upwards => {} State::Flapping => { - chicken - .object - .set_tile_id(frame_ranger(frame_count, 4, 5, 5)); + chicken.object.set_sprite( + object + .get_sprite(&ChickenSprites[frame_ranger(frame_count, 4, 5, 5)]) + .unwrap(), + ); } } @@ -232,16 +244,65 @@ fn handle_collision( // Below is the data for the sprites -static CHICKEN_TILES: [u32; 8 * 6] = [ - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00001000, 0x00001000, 0x00011000, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00010100, 0x00100100, 0x00000010, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00100110, 0x00100000, - 0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00011100, 0x00001000, - 0x01100000, 0x11111100, 0x01111010, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000, - 0x01100000, 0x11100000, 0x01111110, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000, -]; +static ChickenPalette: Palette16 = + Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); -static CHICKEN_PALETTE: [u16; 1] = [0x7C1E]; +static ChickenSprites: &[Sprite] = &[ + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x10, 0x01, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, + 0x10, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00, + 0x00, 0x00, 0x10, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, + 0x00, 0x10, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, + Sprite { + palette: &ChickenPalette, + data: &[ + 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11, + 0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + size: Size::S8x8, + }, +]; static MAP_TILES: [u32; 8 * 17] = [ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index e4ce5206..fff057bb 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -1,9 +1,10 @@ use crate::memory_mapped::MemoryMapped; use bitflags::bitflags; +use modular_bitfield::BitfieldSpecifier; use video::Video; -use self::object::ObjectControl; +use self::object::ObjectController; /// Graphics mode 0. Four regular backgrounds. pub mod background; @@ -69,8 +70,8 @@ pub struct Display { pub struct ObjectDistribution {} impl ObjectDistribution { - pub fn get(&mut self) -> ObjectControl { - ObjectControl::new() + pub fn get(&mut self) -> ObjectController { + ObjectController::new() } } @@ -109,6 +110,7 @@ pub fn busy_wait_for_vblank() { while VCOUNT.get() < 160 {} } +#[derive(BitfieldSpecifier)] pub enum Priority { P0 = 0, P1 = 1, diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 98ce4cdf..9b4b1f78 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use core::alloc::Layout; use core::cell::RefCell; use core::ptr::NonNull; +use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; +use modular_bitfield::{bitfield, BitfieldSpecifier}; use hashbrown::{hash_map::Entry, HashMap}; @@ -13,6 +15,8 @@ use crate::agb_alloc::block_allocator::BlockAllocator; use crate::agb_alloc::bump_allocator::StartEnd; use crate::fixnum::Vector2D; +use attributes::*; + static SPRITE_ALLOCATOR: BlockAllocator = unsafe { BlockAllocator::new(StartEnd { start: || TILE_SPRITE, @@ -73,6 +77,9 @@ impl Size { Size::S32x64 => 32, } } + const fn shape_size(self) -> (u8, u8) { + (self as u8 >> 2, self as u8 & 0b11) + } } pub struct SpriteBorrow<'a> { @@ -104,11 +111,33 @@ impl Storage { fn as_palette_ptr(&self) -> *mut u8 { (self.location as usize * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 } + fn as_sprite_ptr(&self) -> *mut u8 { + (self.location as usize * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 + } +} + +struct Attributes { + a0: ObjectAttribute0, + a1s: ObjectAttribute1Standard, + a1a: ObjectAttribute1Affine, + a2: ObjectAttribute2, +} + +impl Attributes { + fn new() -> Self { + Self { + a0: ObjectAttribute0::new(), + a1s: ObjectAttribute1Standard::new(), + a1a: ObjectAttribute1Affine::new(), + a2: ObjectAttribute2::new(), + } + } } pub struct Object<'a, 'b> { sprite: SpriteBorrow<'a>, loan: Loan<'b>, + attrs: Attributes, } struct SpriteControllerInner { @@ -140,6 +169,17 @@ pub struct ObjectController { impl ObjectController { pub(crate) fn new() -> Self { + DISPLAY_CONTROL.set_bits(1, 1, 0x6); + DISPLAY_CONTROL.set_bits(1, 1, 0xC); + + for i in 0..128 { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16) + .add(i * 4) + .write_volatile(0b10 << 8) + } + } + Self { free_objects: RefCell::new((0..128).collect()), free_affine_matricies: RefCell::new((0..32).collect()), @@ -153,7 +193,11 @@ impl ObjectController { index: inner.pop()?, free_list: &self.free_objects, }; - Some(Object { sprite, loan }) + Some(Object { + sprite, + loan, + attrs: Attributes::new(), + }) } pub fn get_sprite(&self, sprite: &'static Sprite) -> Option { @@ -161,10 +205,80 @@ impl ObjectController { } } +impl Drop for Object<'_, '_> { + fn drop(&mut self) { + self.attrs.a0.set_object_mode(ObjectMode::Disabled); + self.commit(); + } +} + impl<'a, 'b> Object<'a, 'b> { - pub fn set_sprite(&'a mut self, sprite: SpriteBorrow<'a>) { + pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) { + self.attrs.a2.set_tile_index(sprite.sprite_location); + let shape_size = sprite.id.get_sprite().size.shape_size(); + self.attrs.a0.set_shape(shape_size.0); + self.attrs.a1a.set_size(shape_size.1); + self.attrs.a1s.set_size(shape_size.1); self.sprite = sprite; } + + pub fn show(&mut self) -> &mut Self { + self.attrs.a0.set_object_mode(ObjectMode::Normal); + + self + } + + pub fn set_hflip(&mut self, flip: bool) -> &mut Self { + self.attrs.a1s.set_horizontal_flip(flip); + self + } + + pub fn set_vflip(&mut self, flip: bool) -> &mut Self { + self.attrs.a1s.set_vertical_flip(flip); + self + } + + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.attrs.a1a.set_x(x as u16); + self.attrs.a1s.set_x(x as u16); + self + } + + pub fn set_y(&mut self, y: u16) -> &mut Self { + self.attrs.a0.set_y(y as u8); + + self + } + + pub fn set_position(&mut self, position: Vector2D) -> &mut Self { + self.attrs.a0.set_y(position.y as u8); + self.attrs.a1a.set_x(position.x as u16); + self.attrs.a1s.set_x(position.x as u16); + self + } + + pub fn commit(&self) { + let mode = self.attrs.a0.object_mode(); + let attrs: [[u8; 2]; 3] = match mode { + ObjectMode::Normal => [ + self.attrs.a0.into_bytes(), + self.attrs.a1s.into_bytes(), + self.attrs.a2.into_bytes(), + ], + _ => [ + self.attrs.a0.into_bytes(), + self.attrs.a1a.into_bytes(), + self.attrs.a2.into_bytes(), + ], + }; + + unsafe { + let attrs: [u8; 6] = core::mem::transmute(attrs); + (OBJECT_ATTRIBUTE_MEMORY as *mut u8) + .add(self.loan.index as usize * (4 * 2)) + .copy_from(attrs.as_ptr(), attrs.len()) + }; + } } /// The Sprite Id is a thin wrapper around the pointer to the sprite in @@ -288,6 +402,26 @@ impl SpriteControllerInner { } } + fn return_sprite(&mut self, sprite: &'static Sprite) { + let entry = self + .sprite + .entry(sprite.get_id()) + .and_replace_entry_with(|_, mut storage| { + storage.count -= 1; + if storage.count == 0 { + unsafe { SPRITE_ALLOCATOR.dealloc(storage.as_sprite_ptr(), sprite.layout()) } + None + } else { + Some(storage) + } + }); + + match entry { + Entry::Occupied(_) => {} + Entry::Vacant(_) => self.return_palette(sprite.palette), + } + } + fn return_palette(&mut self, palette: &'static Palette16) { let id = palette.get_id(); self.palette @@ -309,21 +443,70 @@ impl SpriteControllerInner { impl<'a> Drop for SpriteBorrow<'a> { fn drop(&mut self) { let mut 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) - } - }); - - match entry { - Entry::Occupied(_) => {} - Entry::Vacant(_) => inner.return_palette(self.id.get_sprite().palette), - } + inner.return_sprite(self.id.get_sprite()) + } +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +enum ObjectMode { + Normal, + Affine, + Disabled, + AffineDouble, +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +#[bits = 2] +enum GraphicsMode { + Normal, + AlphaBlending, + Window, +} + +#[derive(BitfieldSpecifier, Clone, Copy)] +enum ColourMode { + Four, + Eight, +} + +#[allow(dead_code)] +mod attributes { + use super::*; + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute0 { + pub y: B8, + pub object_mode: ObjectMode, + pub graphics_mode: GraphicsMode, + pub mosaic: bool, + pub colour_mode: ColourMode, + pub shape: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute1Standard { + pub x: B9, + #[skip] + __: B3, + pub horizontal_flip: bool, + pub vertical_flip: bool, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute1Affine { + pub x: B9, + pub affine_index: B5, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy)] + pub(super) struct ObjectAttribute2 { + pub tile_index: B10, + pub priority: Priority, + pub palete_bank: B4, } } diff --git a/agb/src/syscall.rs b/agb/src/syscall.rs index bf271981..805e2469 100644 --- a/agb/src/syscall.rs +++ b/agb/src/syscall.rs @@ -1,6 +1,6 @@ use core::arch::asm; -use crate::display::object::AffineMatrixAttributes; +// use crate::display::object::AffineMatrixAttributes; use crate::fixnum::Num; #[allow(non_snake_case)] @@ -113,55 +113,55 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 { result } -pub fn affine_matrix( - x_scale: Num, - y_scale: Num, - rotation: u8, -) -> AffineMatrixAttributes { - let mut result = AffineMatrixAttributes { - p_a: 0, - p_b: 0, - p_c: 0, - p_d: 0, - }; +// pub fn affine_matrix( +// x_scale: Num, +// y_scale: Num, +// rotation: u8, +// ) -> AffineMatrixAttributes { +// let mut result = AffineMatrixAttributes { +// p_a: 0, +// p_b: 0, +// p_c: 0, +// p_d: 0, +// }; - #[allow(dead_code)] - #[repr(C, packed)] - struct Input { - x_scale: i16, - y_scale: i16, - rotation: u16, - } +// #[allow(dead_code)] +// #[repr(C, packed)] +// struct Input { +// x_scale: i16, +// y_scale: i16, +// rotation: u16, +// } - let input = Input { - y_scale: x_scale.to_raw(), - x_scale: y_scale.to_raw(), - rotation: rotation as u16, - }; +// let input = Input { +// y_scale: x_scale.to_raw(), +// x_scale: y_scale.to_raw(), +// rotation: rotation as u16, +// }; - unsafe { - asm!("swi 0x0F", - in("r0") &input as *const Input as usize, - in("r1") &mut result as *mut AffineMatrixAttributes as usize, - in("r2") 1, - in("r3") 2, - ) - } +// unsafe { +// asm!("swi 0x0F", +// in("r0") &input as *const Input as usize, +// in("r1") &mut result as *mut AffineMatrixAttributes as usize, +// in("r2") 1, +// in("r3") 2, +// ) +// } - result -} +// result +// } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - #[test_case] - fn affine(_gba: &mut crate::Gba) { - // expect identity matrix - let one: Num = 1.into(); +// #[test_case] +// fn affine(_gba: &mut crate::Gba) { +// // expect identity matrix +// let one: Num = 1.into(); - let aff = affine_matrix(one, one, 0); - assert_eq!(aff.p_a, one.to_raw()); - assert_eq!(aff.p_d, one.to_raw()); - } -} +// let aff = affine_matrix(one, one, 0); +// assert_eq!(aff.p_a, one.to_raw()); +// assert_eq!(aff.p_d, one.to_raw()); +// } +// }