objects now exist and work

This commit is contained in:
Corwin 2022-02-12 23:57:10 +00:00
parent a1b67e107f
commit 2a7b350f57
6 changed files with 370 additions and 95 deletions

28
agb/Cargo.lock generated
View file

@ -25,6 +25,7 @@ dependencies = [
"bare-metal", "bare-metal",
"bitflags", "bitflags",
"hashbrown", "hashbrown",
"modular-bitfield",
] ]
[[package]] [[package]]
@ -192,6 +193,27 @@ dependencies = [
"adler", "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]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -289,6 +311,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"

View file

@ -27,6 +27,7 @@ agb_macros = { version = "0.1.0", path = "../agb-macros" }
agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" } agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" }
bare-metal = "1.0" bare-metal = "1.0"
hashbrown = "0.12.0" hashbrown = "0.12.0"
modular-bitfield = "0.11.2"
[package.metadata.docs.rs] [package.metadata.docs.rs]
default-target = "thumbv6m-none-eabi" default-target = "thumbv6m-none-eabi"

View file

@ -2,7 +2,12 @@
#![no_main] #![no_main]
use agb::{ use agb::{
display::{background::Map, object::ObjectStandard, HEIGHT, WIDTH}, display::{
background::Map,
object::{Object, ObjectController, Size, Sprite},
palette16::Palette16,
HEIGHT, WIDTH,
},
input::Button, input::Button,
}; };
use core::convert::TryInto; use core::convert::TryInto;
@ -15,7 +20,7 @@ enum State {
} }
struct Character<'a> { struct Character<'a> {
object: ObjectStandard<'a>, object: Object<'a, 'a>,
position: Vector2D, position: Vector2D,
velocity: Vector2D, velocity: Vector2D,
} }
@ -30,8 +35,8 @@ fn tile_is_collidable(tile: u16) -> bool {
masked == 0 || masked == 4 masked == 0 || masked == 4
} }
fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 { fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> usize {
(((count / delay) % (end + 1 - start)) + start) as u16 (((count / delay) % (end + 1 - start)) + start) as usize
} }
#[agb::entry] #[agb::entry]
@ -54,14 +59,11 @@ fn main(mut gba: agb::Gba) -> ! {
background.show(); background.show();
background.commit(); background.commit();
let mut object = gba.display.object.get(); let object = gba.display.object.get();
object.set_sprite_palette_raw(&CHICKEN_PALETTE); let sprite = object.get_sprite(&ChickenSprites[0]).unwrap();
object.set_sprite_tilemap(&CHICKEN_TILES);
object.enable();
let mut chicken = Character { let mut chicken = Character {
object: object.get_object_standard(), object: object.get_object(sprite).unwrap(),
position: Vector2D { position: Vector2D {
x: (6 * 8) << 8, x: (6 * 8) << 8,
y: ((7 * 8) - 4) << 8, y: ((7 * 8) - 4) << 8,
@ -69,7 +71,6 @@ fn main(mut gba: agb::Gba) -> ! {
velocity: Vector2D { x: 0, y: 0 }, velocity: Vector2D { x: 0, y: 0 },
}; };
chicken.object.set_tile_id(0);
chicken chicken
.object .object
.set_x((chicken.position.x >> 8).try_into().unwrap()); .set_x((chicken.position.x >> 8).try_into().unwrap());
@ -120,14 +121,19 @@ fn main(mut gba: agb::Gba) -> ! {
} }
restrict_to_screen(&mut chicken); 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 // Commit the chicken to vram
chicken.object.commit(); 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 { if chicken.velocity.x > 1 {
chicken.object.set_hflip(false); chicken.object.set_hflip(false);
} else if chicken.velocity.x < -1 { } else if chicken.velocity.x < -1 {
@ -136,18 +142,24 @@ fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32
match state { match state {
State::Ground => { State::Ground => {
if chicken.velocity.x.abs() > 1 << 4 { 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 chicken
.object .object
.set_tile_id(frame_ranger(frame_count, 1, 3, 10)); .set_sprite(object.get_sprite(&ChickenSprites[0]).unwrap());
} else {
chicken.object.set_tile_id(0);
} }
} }
State::Upwards => {} State::Upwards => {}
State::Flapping => { State::Flapping => {
chicken chicken.object.set_sprite(
.object object
.set_tile_id(frame_ranger(frame_count, 4, 5, 5)); .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 // Below is the data for the sprites
static CHICKEN_TILES: [u32; 8 * 6] = [ static ChickenPalette: Palette16 =
0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00001000, 0x00001000, 0x00011000, Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
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 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] = [ static MAP_TILES: [u32; 8 * 17] = [
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,

View file

@ -1,9 +1,10 @@
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
use bitflags::bitflags; use bitflags::bitflags;
use modular_bitfield::BitfieldSpecifier;
use video::Video; use video::Video;
use self::object::ObjectControl; use self::object::ObjectController;
/// Graphics mode 0. Four regular backgrounds. /// Graphics mode 0. Four regular backgrounds.
pub mod background; pub mod background;
@ -69,8 +70,8 @@ pub struct Display {
pub struct ObjectDistribution {} pub struct ObjectDistribution {}
impl ObjectDistribution { impl ObjectDistribution {
pub fn get(&mut self) -> ObjectControl { pub fn get(&mut self) -> ObjectController {
ObjectControl::new() ObjectController::new()
} }
} }
@ -109,6 +110,7 @@ pub fn busy_wait_for_vblank() {
while VCOUNT.get() < 160 {} while VCOUNT.get() < 160 {}
} }
#[derive(BitfieldSpecifier)]
pub enum Priority { pub enum Priority {
P0 = 0, P0 = 0,
P1 = 1, P1 = 1,

View file

@ -2,6 +2,8 @@ use alloc::vec::Vec;
use core::alloc::Layout; use core::alloc::Layout;
use core::cell::RefCell; use core::cell::RefCell;
use core::ptr::NonNull; 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}; 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::agb_alloc::bump_allocator::StartEnd;
use crate::fixnum::Vector2D; use crate::fixnum::Vector2D;
use attributes::*;
static SPRITE_ALLOCATOR: BlockAllocator = unsafe { static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
BlockAllocator::new(StartEnd { BlockAllocator::new(StartEnd {
start: || TILE_SPRITE, start: || TILE_SPRITE,
@ -73,6 +77,9 @@ impl Size {
Size::S32x64 => 32, Size::S32x64 => 32,
} }
} }
const fn shape_size(self) -> (u8, u8) {
(self as u8 >> 2, self as u8 & 0b11)
}
} }
pub struct SpriteBorrow<'a> { pub struct SpriteBorrow<'a> {
@ -104,11 +111,33 @@ impl Storage {
fn as_palette_ptr(&self) -> *mut u8 { fn as_palette_ptr(&self) -> *mut u8 {
(self.location as usize * Palette16::layout().size() + PALETTE_SPRITE) as *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> { pub struct Object<'a, 'b> {
sprite: SpriteBorrow<'a>, sprite: SpriteBorrow<'a>,
loan: Loan<'b>, loan: Loan<'b>,
attrs: Attributes,
} }
struct SpriteControllerInner { struct SpriteControllerInner {
@ -140,6 +169,17 @@ pub struct ObjectController {
impl ObjectController { impl ObjectController {
pub(crate) fn new() -> Self { 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 { Self {
free_objects: RefCell::new((0..128).collect()), free_objects: RefCell::new((0..128).collect()),
free_affine_matricies: RefCell::new((0..32).collect()), free_affine_matricies: RefCell::new((0..32).collect()),
@ -153,7 +193,11 @@ impl ObjectController {
index: inner.pop()?, index: inner.pop()?,
free_list: &self.free_objects, 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<SpriteBorrow> { pub fn get_sprite(&self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
@ -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> { 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; 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<i32>) -> &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 /// 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) { fn return_palette(&mut self, palette: &'static Palette16) {
let id = palette.get_id(); let id = palette.get_id();
self.palette self.palette
@ -309,21 +443,70 @@ impl SpriteControllerInner {
impl<'a> Drop for SpriteBorrow<'a> { impl<'a> Drop for SpriteBorrow<'a> {
fn drop(&mut self) { fn drop(&mut self) {
let mut inner = self.controller.borrow_mut(); let mut inner = self.controller.borrow_mut();
let entry = inner inner.return_sprite(self.id.get_sprite())
.sprite }
.entry(self.id) }
.and_replace_entry_with(|_, mut storage| {
storage.count -= 1; #[derive(BitfieldSpecifier, Clone, Copy)]
if storage.count == 0 { enum ObjectMode {
None Normal,
} else { Affine,
Some(storage) Disabled,
} AffineDouble,
}); }
match entry { #[derive(BitfieldSpecifier, Clone, Copy)]
Entry::Occupied(_) => {} #[bits = 2]
Entry::Vacant(_) => inner.return_palette(self.id.get_sprite().palette), 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,
} }
} }

View file

@ -1,6 +1,6 @@
use core::arch::asm; use core::arch::asm;
use crate::display::object::AffineMatrixAttributes; // use crate::display::object::AffineMatrixAttributes;
use crate::fixnum::Num; use crate::fixnum::Num;
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -113,55 +113,55 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
result result
} }
pub fn affine_matrix( // pub fn affine_matrix(
x_scale: Num<i16, 8>, // x_scale: Num<i16, 8>,
y_scale: Num<i16, 8>, // y_scale: Num<i16, 8>,
rotation: u8, // rotation: u8,
) -> AffineMatrixAttributes { // ) -> AffineMatrixAttributes {
let mut result = AffineMatrixAttributes { // let mut result = AffineMatrixAttributes {
p_a: 0, // p_a: 0,
p_b: 0, // p_b: 0,
p_c: 0, // p_c: 0,
p_d: 0, // p_d: 0,
}; // };
#[allow(dead_code)] // #[allow(dead_code)]
#[repr(C, packed)] // #[repr(C, packed)]
struct Input { // struct Input {
x_scale: i16, // x_scale: i16,
y_scale: i16, // y_scale: i16,
rotation: u16, // rotation: u16,
} // }
let input = Input { // let input = Input {
y_scale: x_scale.to_raw(), // y_scale: x_scale.to_raw(),
x_scale: y_scale.to_raw(), // x_scale: y_scale.to_raw(),
rotation: rotation as u16, // rotation: rotation as u16,
}; // };
unsafe { // unsafe {
asm!("swi 0x0F", // asm!("swi 0x0F",
in("r0") &input as *const Input as usize, // in("r0") &input as *const Input as usize,
in("r1") &mut result as *mut AffineMatrixAttributes as usize, // in("r1") &mut result as *mut AffineMatrixAttributes as usize,
in("r2") 1, // in("r2") 1,
in("r3") 2, // in("r3") 2,
) // )
} // }
result // result
} // }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
#[test_case] // #[test_case]
fn affine(_gba: &mut crate::Gba) { // fn affine(_gba: &mut crate::Gba) {
// expect identity matrix // // expect identity matrix
let one: Num<i16, 8> = 1.into(); // let one: Num<i16, 8> = 1.into();
let aff = affine_matrix(one, one, 0); // let aff = affine_matrix(one, one, 0);
assert_eq!(aff.p_a, one.to_raw()); // assert_eq!(aff.p_a, one.to_raw());
assert_eq!(aff.p_d, one.to_raw()); // assert_eq!(aff.p_d, one.to_raw());
} // }
} // }