agb 0.23.1

Library for Game Boy Advance Development
Documentation
use core::{alloc::Layout, ptr::NonNull};

use alloc::alloc::Allocator;

use crate::display::tiled::{
    AffineBackgroundSize, RegularBackgroundSize, SCREENBLOCK_SIZE, ScreenblockAllocator, Tile,
    VRAM_START,
    tiles::{TileInfo, Tiles},
};

pub(crate) struct Screenblock<Size>
where
    Size: ScreenblockSize,
{
    ptr: NonNull<u8>,
    size: Size,
}

pub(crate) trait ScreenblockSize: Copy {
    type TileType: TileInfo;

    fn layout(self) -> Layout;
    fn num_tiles(self) -> usize;
}

impl<Size> Screenblock<Size>
where
    Size: ScreenblockSize,
{
    pub(crate) fn new(size: Size) -> Self {
        let screenblock_ptr = ScreenblockAllocator
            .allocate(size.layout())
            .expect("Not enough space to allocate for background")
            .cast();

        Self {
            ptr: screenblock_ptr,
            size,
        }
    }

    pub(crate) unsafe fn copy_tiles(&self, tiles: &Tiles<Size::TileType>) {
        unsafe {
            self.ptr
                .as_ptr()
                .cast::<Size::TileType>()
                .copy_from_nonoverlapping(tiles.as_ptr(), self.size.num_tiles());
        }
    }

    pub(crate) fn size(&self) -> Size {
        self.size
    }

    pub(crate) fn screen_base_block(&self) -> u16 {
        let screenblock_location = self.ptr.as_ptr() as usize;
        ((screenblock_location - VRAM_START) / SCREENBLOCK_SIZE) as u16
    }

    pub(crate) fn ptr(&self) -> NonNull<u8> {
        self.ptr
    }
}

impl<Size> Drop for Screenblock<Size>
where
    Size: ScreenblockSize,
{
    fn drop(&mut self) {
        unsafe {
            ScreenblockAllocator.deallocate(self.ptr, self.size.layout());
        }
    }
}

impl ScreenblockSize for RegularBackgroundSize {
    type TileType = Tile;

    fn layout(self) -> Layout {
        RegularBackgroundSize::layout(self)
    }

    fn num_tiles(self) -> usize {
        RegularBackgroundSize::num_tiles(self)
    }
}

impl ScreenblockSize for AffineBackgroundSize {
    type TileType = u8;

    fn layout(self) -> Layout {
        AffineBackgroundSize::layout(self)
    }

    fn num_tiles(self) -> usize {
        AffineBackgroundSize::num_tiles(self)
    }
}