diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 6eddc02b..a307db4c 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -4,6 +4,7 @@ extern crate agb; use agb::display::example_logo; +use agb::display::tiled0::Map; #[no_mangle] pub fn main() -> ! { diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 7b9c520d..4653c00b 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -2,6 +2,8 @@ use crate::display::tiled0::Tiled0; crate::include_gfx!("gfx/agb_logo.toml"); +use super::tiled0::Map; + pub fn display_logo(gfx: &mut Tiled0) { gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); @@ -14,7 +16,11 @@ pub fn display_logo(gfx: &mut Tiled0) { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); + back.set_map(Map { + store: &mut entries, + dimensions: (30_u32, 20_u32).into(), + default: 0, + }); back.show(); } diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index b79ffd49..64fb5a99 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -1,5 +1,3 @@ -use core::{convert::TryInto, ops::Deref}; - use crate::{ memory_mapped::{MemoryMapped, MemoryMapped1DArray}, number::{Rect, Vector2D}, @@ -12,13 +10,9 @@ use super::{ const PALETTE_BACKGROUND: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x0500_0000) }; -const PALETTE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x0500_0200) }; const TILE_BACKGROUND: MemoryMapped1DArray = unsafe { MemoryMapped1DArray::new(0x06000000) }; -const TILE_SPRITE: MemoryMapped1DArray = - unsafe { MemoryMapped1DArray::new(0x06010000) }; const MAP: *mut [[[u16; 32]; 32]; 32] = 0x0600_0000 as *mut _; @@ -37,17 +31,36 @@ pub enum BackgroundSize { /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background { +pub struct Background<'a> { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, + map: Option>, } -impl Background { - unsafe fn new(background: u8, block: u8) -> Background { +pub struct Map<'a> { + pub store: &'a mut [u16], + pub dimensions: Vector2D, + pub default: u16, +} + +impl<'a> Map<'a> { + fn get_position(&self, x: i32, y: i32) -> u16 { + if x < 0 || x as u32 >= self.dimensions.x { + self.default + } else if y < 0 || y as u32 >= self.dimensions.y { + self.default + } else { + self.store[y as usize * self.dimensions.x as usize + x as usize] + } + } +} + +impl<'a> Background<'a> { + unsafe fn new(background: u8, block: u8) -> Background<'a> { let mut b = Background { background, block, @@ -55,6 +68,7 @@ impl Background { shadowed_position: (0, 0).into(), shadowed_register: 0, poisoned: true, + map: None, }; b.set_block(block); b.set_colour_mode(ColourMode::FourBitPerPixel); @@ -62,6 +76,22 @@ impl Background { b } + /// Sets the background to be shown on screen. Requires the background to + /// have a map enabled otherwise a panic is caused. + pub fn show(&mut self) { + assert!(self.map.is_some()); + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode | (1 << (self.background + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + + /// Hides the background, nothing from this background is rendered to screen. + pub fn hide(&mut self) { + let mode = DISPLAY_CONTROL.get(); + let new_mode = mode & !(1 << (self.background + 0x08)); + DISPLAY_CONTROL.set(new_mode); + } + unsafe fn set_shadowed_register_bits(&mut self, value: u16, length: u16, shift: u16) { let mask = !(((1 << length) - 1) << shift); let new = (self.shadowed_register & mask) | (value << shift); @@ -88,15 +118,27 @@ impl Background { self.set_shadowed_register_bits(size as u16, 0x2, 0xE); } + unsafe fn set_position_x_register(&self, x: u16) { + *((0x0400_0010 + 4 * self.background as usize) as *mut u16) = x + } + unsafe fn set_position_y_register(&self, y: u16) { + *((0x0400_0012 + 4 * self.background as usize) as *mut u16) = y + } + pub fn set_position(&mut self, position: Vector2D) { self.shadowed_position = position; } - pub fn map_has_changed(&mut self) { + pub fn get_map(&mut self) -> &mut Option> { self.poisoned = true; + &mut self.map } - pub fn commit_area(&self, map: &[u16], map_dimensions: Vector2D, area: Rect) { + pub fn set_map(&mut self, map: Map<'a>) { + self.map = Some(map); + } + + pub fn commit_area(&mut self, area: Rect) { // commit shadowed register unsafe { self.get_register().set(self.shadowed_register) }; @@ -143,19 +185,37 @@ impl Background { y_update.iter().chain(x_update.iter()) }; - for (x, y) in positions_to_be_updated {} + if let Some(map) = &self.map { + for (x, y) in positions_to_be_updated { + let tile_space_position = self.shadowed_position / 8; + unsafe { + (&mut (*MAP)[self.block as usize][y.rem_euclid(32) as usize] + [x.rem_euclid(32) as usize] as *mut u16) + .write_volatile( + map.get_position(x + tile_space_position.x, y + tile_space_position.y), + ); + } + } + } // update commited position + self.commited_position = self.shadowed_position; + // update position in registers + + unsafe { + self.set_position_x_register((self.commited_position.x % (32 * 8)) as u16); + self.set_position_y_register((self.commited_position.y % (32 * 8)) as u16); + } } - pub fn commit(&self, map: &[u16], map_dimensions: Vector2D) { + pub fn commit(&mut self) { let area: Rect = Rect { position: Vector2D::new(-1, -1), size: Vector2D::new(32, 22), }; - self.commit_area(map, map_dimensions, area) + self.commit_area(area) } }