diff --git a/agb/src/display/background.rs b/agb/src/display/background.rs index 8bbafc2..7ee8b6b 100644 --- a/agb/src/display/background.rs +++ b/agb/src/display/background.rs @@ -1,19 +1,13 @@ -use core::cell::RefCell; -use core::ops::{Deref, DerefMut}; - use alloc::boxed::Box; pub use super::tiled::VRamManager; -use super::tiled::{RegularMap, TileSetReference, TileSetting}; +use super::tiled::{MapLoan, RegularMap, TileSetReference, TileSetting}; -use crate::bitarray::Bitarray; use crate::{ display, fixnum::{Rect, Vector2D}, }; -use super::{set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, Priority}; - pub struct InfiniteScrolledMap<'a> { map: MapLoan<'a, RegularMap>, get_tile: Box) -> (TileSetReference, TileSetting)>, @@ -241,70 +235,3 @@ fn div_ceil(x: i32, y: i32) -> i32 { x / y } } - -pub struct Tiled0 { - regular: RefCell>, -} - -impl Tiled0 { - pub(crate) unsafe fn new() -> Self { - set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); - set_graphics_mode(DisplayMode::Tiled0); - - Self { - regular: Default::default(), - } - } - - pub fn background(&self, priority: Priority) -> MapLoan<'_, RegularMap> { - let mut regular = self.regular.borrow_mut(); - let new_background = regular.first_zero().unwrap(); - if new_background >= 4 { - panic!("can only have 4 active backgrounds"); - } - - let bg = RegularMap::new(new_background as u8, (new_background + 16) as u8, priority); - - regular.set(new_background, true); - - MapLoan::new(bg, new_background as u8, &self.regular) - } -} - -pub struct MapLoan<'a, T> { - map: T, - background_id: u8, - regular_map_list: &'a RefCell>, -} - -impl<'a, T> Deref for MapLoan<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.map - } -} - -impl<'a, T> DerefMut for MapLoan<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.map - } -} - -impl<'a, T> MapLoan<'a, T> { - fn new(map: T, background_id: u8, regular_map_list: &'a RefCell>) -> Self { - MapLoan { - map, - background_id, - regular_map_list, - } - } -} - -impl<'a, T> Drop for MapLoan<'a, T> { - fn drop(&mut self) { - self.regular_map_list - .borrow_mut() - .set(self.background_id as usize, false); - } -} diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs new file mode 100644 index 0000000..f7271dc --- /dev/null +++ b/agb/src/display/tiled/map.rs @@ -0,0 +1,198 @@ +use core::cell::RefCell; +use core::ops::{Deref, DerefMut}; + +use crate::bitarray::Bitarray; +use crate::display::{self, Priority, DISPLAY_CONTROL}; +use crate::fixnum::Vector2D; +use crate::memory_mapped::{MemoryMapped, MemoryMapped1DArray}; + +use super::{Tile, TileSetReference, TileSetting, VRamManager}; + +pub struct RegularMap { + background_id: u8, + + screenblock: u8, + x_scroll: u16, + y_scroll: u16, + priority: Priority, + + tiles: [Tile; 32 * 32], + tiles_dirty: bool, +} + +pub const TRANSPARENT_TILE_INDEX: u16 = (1 << 10) - 1; + +impl RegularMap { + pub(crate) fn new(background_id: u8, screenblock: u8, priority: Priority) -> Self { + Self { + background_id, + + screenblock, + x_scroll: 0, + y_scroll: 0, + priority, + + tiles: [Tile::default(); 32 * 32], + tiles_dirty: true, + } + } + + pub fn set_tile( + &mut self, + vram: &mut VRamManager, + pos: Vector2D, + tileset_ref: TileSetReference, + tile_setting: TileSetting, + ) { + let pos = (pos.x + pos.y * 32) as usize; + + let old_tile = self.tiles[pos]; + if old_tile != Tile::default() { + vram.remove_tile(old_tile.tile_index()); + } + + let tile_index = tile_setting.index(); + + let new_tile = if tile_index != TRANSPARENT_TILE_INDEX { + let new_tile_idx = vram.add_tile(tileset_ref, tile_index); + Tile::new(new_tile_idx, tile_setting) + } else { + Tile::default() + }; + + if old_tile == new_tile { + // no need to mark as dirty if nothing changes + return; + } + + self.tiles[pos] = new_tile; + self.tiles_dirty = true; + } + + pub fn clear(&mut self, vram: &mut VRamManager) { + for tile in self.tiles.iter_mut() { + vram.remove_tile(tile.tile_index()); + + *tile = Tile::default(); + } + } + + pub fn show(&mut self) { + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode | (1 << (self.background_id + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + + pub fn hide(&mut self) { + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode & !(1 << (self.background_id + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + + pub fn commit(&mut self) { + let new_bg_control_value = (self.priority as u16) | ((self.screenblock as u16) << 8); + + self.bg_control_register().set(new_bg_control_value); + self.bg_h_offset().set(self.x_scroll); + self.bg_v_offset().set(self.y_scroll); + + if !self.tiles_dirty { + return; + } + + let screenblock_memory = self.screenblock_memory(); + + let scroll_pos = self.get_scroll_pos(); + let start_x = scroll_pos.x / 8; + let end_x = div_ceil(scroll_pos.x as i32 + display::WIDTH, 8) as u16 + 1; + + let start_y = scroll_pos.y / 8; + let end_y = div_ceil(scroll_pos.y as i32 + display::HEIGHT, 8) as u16 + 1; + + for y in start_y..end_y { + for x in start_x..end_x { + let id = y.rem_euclid(32) * 32 + x.rem_euclid(32); + screenblock_memory.set(id as usize, self.tiles[id as usize].0); + } + } + + self.tiles_dirty = false; + } + + pub fn set_scroll_pos(&mut self, pos: Vector2D) { + self.x_scroll = pos.x; + self.y_scroll = pos.y; + } + + pub fn get_scroll_pos(&self) -> Vector2D { + (self.x_scroll, self.y_scroll).into() + } + + const fn bg_control_register(&self) -> MemoryMapped { + unsafe { MemoryMapped::new(0x0400_0008 + 2 * self.background_id as usize) } + } + + const fn bg_h_offset(&self) -> MemoryMapped { + unsafe { MemoryMapped::new(0x0400_0010 + 4 * self.background_id as usize) } + } + + const fn bg_v_offset(&self) -> MemoryMapped { + unsafe { MemoryMapped::new(0x0400_0012 + 4 * self.background_id as usize) } + } + + const fn screenblock_memory(&self) -> MemoryMapped1DArray { + unsafe { MemoryMapped1DArray::new(0x0600_0000 + 0x1000 * self.screenblock as usize / 2) } + } +} + +pub struct MapLoan<'a, T> { + map: T, + background_id: u8, + regular_map_list: &'a RefCell>, +} + +impl<'a, T> Deref for MapLoan<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, T> DerefMut for MapLoan<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + +impl<'a, T> MapLoan<'a, T> { + pub(crate) fn new( + map: T, + background_id: u8, + regular_map_list: &'a RefCell>, + ) -> Self { + MapLoan { + map, + background_id, + regular_map_list, + } + } +} + +impl<'a, T> Drop for MapLoan<'a, T> { + fn drop(&mut self) { + self.regular_map_list + .borrow_mut() + .set(self.background_id as usize, false); + } +} + +fn div_ceil(x: i32, y: i32) -> i32 { + if x > 0 && y > 0 { + (x - 1) / y + 1 + } else if x < 0 && y < 0 { + (x + 1) / y + 1 + } else { + x / y + } +} diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index d88ad60..e8908e4 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -1,7 +1,9 @@ mod map; +mod tiled0; mod vram_manager; -pub use map::RegularMap; +pub use map::{MapLoan, RegularMap}; +pub use tiled0::Tiled0; pub use vram_manager::{TileFormat, TileIndex, TileSet, TileSetReference, VRamManager}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] diff --git a/agb/src/display/tiled/tiled0.rs b/agb/src/display/tiled/tiled0.rs new file mode 100644 index 0000000..86b7387 --- /dev/null +++ b/agb/src/display/tiled/tiled0.rs @@ -0,0 +1,37 @@ +use core::cell::RefCell; + +use crate::{ + bitarray::Bitarray, + display::{set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, Priority}, +}; + +use super::{MapLoan, RegularMap}; + +pub struct Tiled0 { + regular: RefCell>, +} + +impl Tiled0 { + pub(crate) unsafe fn new() -> Self { + set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); + set_graphics_mode(DisplayMode::Tiled0); + + Self { + regular: Default::default(), + } + } + + pub fn background(&self, priority: Priority) -> MapLoan<'_, RegularMap> { + let mut regular = self.regular.borrow_mut(); + let new_background = regular.first_zero().unwrap(); + if new_background >= 4 { + panic!("can only have 4 active backgrounds"); + } + + let bg = RegularMap::new(new_background as u8, (new_background + 16) as u8, priority); + + regular.set(new_background, true); + + MapLoan::new(bg, new_background as u8, &self.regular) + } +} diff --git a/agb/src/display/video.rs b/agb/src/display/video.rs index fce2b57..046ff12 100644 --- a/agb/src/display/video.rs +++ b/agb/src/display/video.rs @@ -1,7 +1,7 @@ use super::{ - background::{Tiled0, VRamManager}, bitmap3::Bitmap3, bitmap4::Bitmap4, + tiled::{Tiled0, VRamManager}, }; #[non_exhaustive]