From 288fa0c85e9f72c1636c85077c37befb4137909d Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:28:32 +0100 Subject: [PATCH 01/16] Add an enum for tracking background size of regular tiles --- agb/src/display/tiled/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index a0ec32d8..9e56513a 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -8,6 +8,42 @@ pub use map::{MapLoan, RegularMap}; pub use tiled0::Tiled0; pub use vram_manager::{DynamicTile, TileFormat, TileIndex, TileSet, VRamManager}; +pub enum RegularBackgroundSize { + Background32x32, + Background64x32, + Background32x64, + Background64x64, +} + +impl RegularBackgroundSize { + pub fn width(&self) -> u32 { + match self { + RegularBackgroundSize::Background32x32 => 32, + RegularBackgroundSize::Background64x32 => 64, + RegularBackgroundSize::Background32x64 => 32, + RegularBackgroundSize::Background64x64 => 64, + } + } + + pub fn height(&self) -> u32 { + match self { + RegularBackgroundSize::Background32x32 => 32, + RegularBackgroundSize::Background64x32 => 32, + RegularBackgroundSize::Background32x64 => 64, + RegularBackgroundSize::Background64x64 => 64, + } + } + + pub(crate) fn size_flag(&self) -> u16 { + match self { + RegularBackgroundSize::Background32x32 => 0, + RegularBackgroundSize::Background64x32 => 1, + RegularBackgroundSize::Background32x64 => 2, + RegularBackgroundSize::Background64x64 => 3, + } + } +} + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[repr(transparent)] struct Tile(u16); From 5dd0c61808ac2521385f18d3c5fc3718a0d4a920 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:30:32 +0100 Subject: [PATCH 02/16] Derive normal stuff on an enum --- agb/src/display/tiled/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 9e56513a..b40a0701 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -8,6 +8,7 @@ pub use map::{MapLoan, RegularMap}; pub use tiled0::Tiled0; pub use vram_manager::{DynamicTile, TileFormat, TileIndex, TileSet, VRamManager}; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RegularBackgroundSize { Background32x32, Background64x32, From d514aafad4d303c96cea9c39d3574247da3bec68 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:34:41 +0100 Subject: [PATCH 03/16] Pass through the size --- agb/src/display/tiled/map.rs | 13 +++++++++++-- agb/src/display/tiled/tiled0.rs | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index 9d6c9790..dc6910f8 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -7,7 +7,7 @@ use crate::dma::dma_copy16; use crate::fixnum::Vector2D; use crate::memory_mapped::MemoryMapped; -use super::{Tile, TileSet, TileSetting, VRamManager}; +use super::{RegularBackgroundSize, Tile, TileSet, TileSetting, VRamManager}; pub struct RegularMap { background_id: u8, @@ -19,12 +19,19 @@ pub struct RegularMap { tiles: [Tile; 32 * 32], tiles_dirty: bool, + + size: RegularBackgroundSize, } pub const TRANSPARENT_TILE_INDEX: u16 = (1 << 10) - 1; impl RegularMap { - pub(crate) fn new(background_id: u8, screenblock: u8, priority: Priority) -> Self { + pub(crate) fn new( + background_id: u8, + screenblock: u8, + priority: Priority, + size: RegularBackgroundSize, + ) -> Self { Self { background_id, @@ -35,6 +42,8 @@ impl RegularMap { tiles: [Tile::default(); 32 * 32], tiles_dirty: true, + + size, } } diff --git a/agb/src/display/tiled/tiled0.rs b/agb/src/display/tiled/tiled0.rs index ad5da7fb..f6c20db8 100644 --- a/agb/src/display/tiled/tiled0.rs +++ b/agb/src/display/tiled/tiled0.rs @@ -5,7 +5,7 @@ use crate::{ display::{set_graphics_mode, DisplayMode, Priority}, }; -use super::{MapLoan, RegularMap}; +use super::{MapLoan, RegularBackgroundSize, RegularMap}; pub struct Tiled0 { regular: RefCell>, @@ -20,14 +20,23 @@ impl Tiled0 { } } - pub fn background(&self, priority: Priority) -> MapLoan<'_, RegularMap> { + pub fn background( + &self, + priority: Priority, + size: RegularBackgroundSize, + ) -> 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); + let bg = RegularMap::new( + new_background as u8, + (new_background + 16) as u8, + priority, + size, + ); regular.set(new_background, true); From 384a5bc88728c4a72d6c81a68d67310b9d1c66ee Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:46:32 +0100 Subject: [PATCH 04/16] Remove a bunch of hard coded widths and heights --- agb/src/display/tiled/infinite_scrolled_map.rs | 18 ++++++++++-------- agb/src/display/tiled/map.rs | 14 ++++++++++---- agb/src/display/tiled/mod.rs | 4 ++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index 48cc66ba..91310d78 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -63,8 +63,8 @@ impl<'a> InfiniteScrolledMap<'a> { let offset = self.current_pos - (x_start * 8, y_start * 8).into(); let offset_scroll = ( - offset.x.rem_euclid(32 * 8) as u16, - offset.y.rem_euclid(32 * 8) as u16, + offset.x.rem_euclid(self.map.size().width() as i32 * 8) as u16, + offset.y.rem_euclid(self.map.size().height() as i32 * 8) as u16, ) .into(); @@ -120,6 +120,8 @@ impl<'a> InfiniteScrolledMap<'a> { let difference_tile_x = div_ceil(difference.x, 8); let difference_tile_y = div_ceil(difference.y, 8); + let size = self.map.size(); + let vertical_rect_to_update: Rect = if div_floor(old_pos.x, 8) != new_tile_x { // need to update the x line // calculate which direction we need to update @@ -149,8 +151,8 @@ impl<'a> InfiniteScrolledMap<'a> { // calculate which direction we need to update let direction = difference.y.signum(); - // either need to update 30 or 31 tiles depending on whether the x coordinate is a perfect multiple - let x_tiles_to_update: i32 = 32; + // either need to update width - 2 or width - 1 tiles depending on whether the x coordinate is a perfect multiple + let x_tiles_to_update: i32 = size.width() as i32; let line_to_update = if direction < 0 { // moving up so need to update the top @@ -177,8 +179,8 @@ impl<'a> InfiniteScrolledMap<'a> { self.map.set_tile( vram, ( - (tile_x - self.offset.x).rem_euclid(32) as u16, - (tile_y - self.offset.y).rem_euclid(32) as u16, + (tile_x - self.offset.x).rem_euclid(size.width() as i32) as u16, + (tile_y - self.offset.y).rem_euclid(size.height() as i32) as u16, ) .into(), tileset, @@ -188,8 +190,8 @@ impl<'a> InfiniteScrolledMap<'a> { let current_scroll = self.map.scroll_pos(); let new_scroll = ( - (current_scroll.x as i32 + difference.x).rem_euclid(32 * 8) as u16, - (current_scroll.y as i32 + difference.y).rem_euclid(32 * 8) as u16, + (current_scroll.x as i32 + difference.x).rem_euclid(size.width() as i32 * 8) as u16, + (current_scroll.y as i32 + difference.y).rem_euclid(size.height() as i32 * 8) as u16, ) .into(); diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index dc6910f8..9f5a8b2b 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -9,6 +9,8 @@ use crate::memory_mapped::MemoryMapped; use super::{RegularBackgroundSize, Tile, TileSet, TileSetting, VRamManager}; +use alloc::{vec, vec::Vec}; + pub struct RegularMap { background_id: u8, @@ -17,7 +19,7 @@ pub struct RegularMap { y_scroll: u16, priority: Priority, - tiles: [Tile; 32 * 32], + tiles: Vec, tiles_dirty: bool, size: RegularBackgroundSize, @@ -40,7 +42,7 @@ impl RegularMap { y_scroll: 0, priority, - tiles: [Tile::default(); 32 * 32], + tiles: vec![Default::default(); size.num_tiles()], tiles_dirty: true, size, @@ -54,7 +56,7 @@ impl RegularMap { tileset: &TileSet<'_>, tile_setting: TileSetting, ) { - let pos = (pos.x + pos.y * 32) as usize; + let pos = (pos.x + pos.y * self.size.width() as u16) as usize; let old_tile = self.tiles[pos]; if old_tile != Tile::default() { @@ -120,7 +122,7 @@ impl RegularMap { dma_copy16( self.tiles.as_ptr() as *const u16, screenblock_memory, - 32 * 32, + self.size.num_tiles(), ); } @@ -136,6 +138,10 @@ impl RegularMap { (self.x_scroll, self.y_scroll).into() } + pub(crate) fn size(&self) -> RegularBackgroundSize { + self.size + } + const fn bg_control_register(&self) -> MemoryMapped { unsafe { MemoryMapped::new(0x0400_0008 + 2 * self.background_id as usize) } } diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index b40a0701..ca10e66f 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -43,6 +43,10 @@ impl RegularBackgroundSize { RegularBackgroundSize::Background64x64 => 3, } } + + pub(crate) fn num_tiles(&self) -> usize { + (self.width() * self.height()) as usize + } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] From cc57dcf649d0704d1117fb40e45a4cb4ec987f49 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:48:08 +0100 Subject: [PATCH 05/16] Fix compile errors --- agb/src/display/example_logo.rs | 4 +++- agb/src/display/font.rs | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index d39389b8..bec4df92 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -23,13 +23,15 @@ pub fn display_logo(map: &mut RegularMap, vram: &mut VRamManager) { } #[cfg(test)] mod tests { + use crate::display::{tiled::RegularBackgroundSize, Priority}; + use super::*; #[test_case] fn logo_display(gba: &mut crate::Gba) { let (gfx, mut vram) = gba.display.video.tiled0(); - let mut map = gfx.background(crate::display::Priority::P0); + let mut map = gfx.background(Priority::P0, RegularBackgroundSize::Background32x32); display_logo(&mut map, &mut vram); diff --git a/agb/src/display/font.rs b/agb/src/display/font.rs index a7058b72..8bbe9ac2 100644 --- a/agb/src/display/font.rs +++ b/agb/src/display/font.rs @@ -213,7 +213,10 @@ mod tests { fn font_display(gba: &mut crate::Gba) { let (gfx, mut vram) = gba.display.video.tiled0(); - let mut bg = gfx.background(crate::display::Priority::P0); + let mut bg = gfx.background( + crate::display::Priority::P0, + crate::display::tiled::RegularBackgroundSize::Background32x32, + ); vram.set_background_palette_raw(&[ 0x0000, 0x0ff0, 0x00ff, 0xf00f, 0xf0f0, 0x0f0f, 0xaaaa, 0x5555, 0x0000, 0x0000, 0x0000, From 4f2eddb36734a92fe753f9b66196a30921470cf7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 21:51:00 +0100 Subject: [PATCH 06/16] Actually set the correct flag --- agb/src/display/tiled/map.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index 9f5a8b2b..c42cbeda 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -104,7 +104,9 @@ impl RegularMap { } pub fn commit(&mut self, vram: &mut VRamManager) { - let new_bg_control_value = (self.priority as u16) | ((self.screenblock as u16) << 8); + let new_bg_control_value = (self.priority as u16) + | ((self.screenblock as u16) << 8) + | (self.size.size_flag() << 14); self.bg_control_register().set(new_bg_control_value); self.bg_h_offset().set(self.x_scroll); From fae71bfbcab0895f4d57270b7a9ed804a1c53e27 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 22:29:51 +0100 Subject: [PATCH 07/16] Fix all the examples --- agb/examples/animated_background.rs | 4 ++-- agb/examples/chicken.rs | 6 +++++- agb/examples/dynamic_tiles.rs | 8 ++++++-- agb/examples/test_logo.rs | 7 +++++-- agb/examples/text_render.rs | 7 +++++-- agb/examples/wave.rs | 7 +++++-- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/agb/examples/animated_background.rs b/agb/examples/animated_background.rs index 2c2a3392..d20213d9 100644 --- a/agb/examples/animated_background.rs +++ b/agb/examples/animated_background.rs @@ -3,7 +3,7 @@ use agb::{ display::{ - tiled::{TileFormat, TileSet, TileSetting}, + tiled::{RegularBackgroundSize, TileFormat, TileSet, TileSetting}, Priority, }, include_gfx, @@ -20,7 +20,7 @@ fn main(mut gba: agb::Gba) -> ! { vram.set_background_palettes(water_tiles::water_tiles.palettes); - let mut bg = gfx.background(Priority::P0); + let mut bg = gfx.background(Priority::P0, RegularBackgroundSize::Background32x32); for y in 0..20u16 { for x in 0..30u16 { diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index d0782e15..02536887 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -6,6 +6,7 @@ use agb::{ display::{ object::{Object, ObjectController, Size, Sprite}, palette16::Palette16, + tiled::RegularBackgroundSize, HEIGHT, WIDTH, }, input::Button, @@ -54,7 +55,10 @@ fn main(mut gba: agb::Gba) -> ! { vram.set_background_palette_raw(&MAP_PALETTE); let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp); - let mut background = gfx.background(agb::display::Priority::P0); + let mut background = gfx.background( + agb::display::Priority::P0, + RegularBackgroundSize::Background32x32, + ); for (i, &tile) in MAP_MAP.iter().enumerate() { let i = i as u16; diff --git a/agb/examples/dynamic_tiles.rs b/agb/examples/dynamic_tiles.rs index 1d746f9f..53b013a4 100644 --- a/agb/examples/dynamic_tiles.rs +++ b/agb/examples/dynamic_tiles.rs @@ -1,7 +1,11 @@ #![no_std] #![no_main] -use agb::display::{palette16::Palette16, tiled::TileSetting, Priority}; +use agb::display::{ + palette16::Palette16, + tiled::{RegularBackgroundSize, TileSetting}, + Priority, +}; #[agb::entry] fn main(mut gba: agb::Gba) -> ! { @@ -13,7 +17,7 @@ fn main(mut gba: agb::Gba) -> ! { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, ])]); - let mut bg = gfx.background(Priority::P0); + let mut bg = gfx.background(Priority::P0, RegularBackgroundSize::Background32x32); for y in 0..20u32 { for x in 0..30u32 { diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 674867c9..464e4d3e 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -1,13 +1,16 @@ #![no_std] #![no_main] -use agb::display::example_logo; +use agb::display::{example_logo, tiled::RegularBackgroundSize}; #[agb::entry] fn main(mut gba: agb::Gba) -> ! { let (gfx, mut vram) = gba.display.video.tiled0(); - let mut map = gfx.background(agb::display::Priority::P0); + let mut map = gfx.background( + agb::display::Priority::P0, + RegularBackgroundSize::Background32x32, + ); example_logo::display_logo(&mut map, &mut vram); diff --git a/agb/examples/text_render.rs b/agb/examples/text_render.rs index 6584e9e7..fa398a1d 100644 --- a/agb/examples/text_render.rs +++ b/agb/examples/text_render.rs @@ -2,7 +2,10 @@ #![no_main] use agb::{ - display::{tiled::TileSetting, Font, Priority}, + display::{ + tiled::{RegularBackgroundSize, TileSetting}, + Font, Priority, + }, include_font, }; @@ -22,7 +25,7 @@ fn main(mut gba: agb::Gba) -> ! { let background_tile = vram.new_dynamic_tile().fill_with(0); - let mut bg = gfx.background(Priority::P0); + let mut bg = gfx.background(Priority::P0, RegularBackgroundSize::Background32x32); for y in 0..20u16 { for x in 0..30u16 { diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index 8fffaddc..7571eef8 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -4,7 +4,7 @@ use core::cell::RefCell; use agb::{ - display::example_logo, + display::{example_logo, tiled::RegularBackgroundSize}, fixnum::FixedNum, interrupt::{free, Interrupt}, }; @@ -19,7 +19,10 @@ struct BackCosines { fn main(mut gba: agb::Gba) -> ! { let (gfx, mut vram) = gba.display.video.tiled0(); - let mut background = gfx.background(agb::display::Priority::P0); + let mut background = gfx.background( + agb::display::Priority::P0, + RegularBackgroundSize::Background32x32, + ); example_logo::display_logo(&mut background, &mut vram); From 5513c8566f1b09c34d264c94e9c0cad09f40d552 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 22:46:01 +0100 Subject: [PATCH 08/16] Replace calls to rem_euclid with unknown denominator with simple & --- .../display/tiled/infinite_scrolled_map.rs | 12 +++---- agb/src/display/tiled/mod.rs | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index 91310d78..e2e90aff 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -63,8 +63,8 @@ impl<'a> InfiniteScrolledMap<'a> { let offset = self.current_pos - (x_start * 8, y_start * 8).into(); let offset_scroll = ( - offset.x.rem_euclid(self.map.size().width() as i32 * 8) as u16, - offset.y.rem_euclid(self.map.size().height() as i32 * 8) as u16, + self.map.size().rem_euclid_width(offset.x) as u16, + self.map.size().rem_euclid_height(offset.y) as u16, ) .into(); @@ -179,8 +179,8 @@ impl<'a> InfiniteScrolledMap<'a> { self.map.set_tile( vram, ( - (tile_x - self.offset.x).rem_euclid(size.width() as i32) as u16, - (tile_y - self.offset.y).rem_euclid(size.height() as i32) as u16, + size.rem_euclid_width(tile_x - self.offset.x) as u16, + size.rem_euclid_height(tile_y - self.offset.y) as u16, ) .into(), tileset, @@ -190,8 +190,8 @@ impl<'a> InfiniteScrolledMap<'a> { let current_scroll = self.map.scroll_pos(); let new_scroll = ( - (current_scroll.x as i32 + difference.x).rem_euclid(size.width() as i32 * 8) as u16, - (current_scroll.y as i32 + difference.y).rem_euclid(size.height() as i32 * 8) as u16, + size.rem_euclid_width(current_scroll.x as i32 + difference.x) as u16, + size.rem_euclid_height(current_scroll.y as i32 + difference.y) as u16, ) .into(); diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index ca10e66f..2fabce54 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -47,6 +47,14 @@ impl RegularBackgroundSize { pub(crate) fn num_tiles(&self) -> usize { (self.width() * self.height()) as usize } + + pub(crate) fn rem_euclid_width(&self, x: i32) -> u32 { + (x as u32) & (self.width() - 1) + } + + pub(crate) fn rem_euclid_height(&self, y: i32) -> u32 { + (y as u32) & (self.height() - 1) + } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -88,3 +96,31 @@ impl TileSetting { self.0 & !((1 << 10) - 1) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test_case] + fn rem_euclid_width_works(_gba: &mut crate::Gba) { + use RegularBackgroundSize::*; + + let sizes = [ + Background32x32, + Background32x64, + Background64x32, + Background64x64, + ]; + + for size in sizes.iter() { + let width = size.width() as i32; + + assert_eq!(size.rem_euclid_width(8), 8); + assert_eq!(size.rem_euclid_width(3 + width), 3); + assert_eq!(size.rem_euclid_width(7 + width * 9), 7); + + assert_eq!(size.rem_euclid_width(-8), size.width() - 8); + assert_eq!(size.rem_euclid_width(-17 - width * 8), size.width() - 17); + } + } +} From 39b37e7506a39f9c1099c2861fab7b7ae9dce1af Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 22:51:01 +0100 Subject: [PATCH 09/16] Fix example games --- examples/the-hat-chooses-the-wizard/src/main.rs | 11 ++++++----- examples/the-purple-night/src/main.rs | 11 +++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index de8f3ea9..57ab5ec2 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -7,7 +7,8 @@ use agb::{ display::{ object::{Graphics, Object, ObjectController, Tag, TagMap}, tiled::{ - InfiniteScrolledMap, PartialUpdateStatus, TileFormat, TileSet, TileSetting, VRamManager, + InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet, + TileSetting, VRamManager, }, Priority, HEIGHT, WIDTH, }, @@ -777,8 +778,8 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { fn main(mut agb: agb::Gba) -> ! { let (tiled, mut vram) = agb.display.video.tiled0(); vram.set_background_palettes(tile_sheet::background.palettes); - let mut splash_screen = tiled.background(Priority::P0); - let mut world_display = tiled.background(Priority::P0); + let mut splash_screen = tiled.background(Priority::P0, RegularBackgroundSize::Background32x32); + let mut world_display = tiled.background(Priority::P0, RegularBackgroundSize::Background32x32); let tileset = TileSet::new(tile_sheet::background.tiles, TileFormat::FourBpp); @@ -845,7 +846,7 @@ fn main(mut agb: agb::Gba) -> ! { let map_current_level = current_level; let mut background = InfiniteScrolledMap::new( - tiled.background(Priority::P2), + tiled.background(Priority::P2, RegularBackgroundSize::Background32x32), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( @@ -860,7 +861,7 @@ fn main(mut agb: agb::Gba) -> ! { }), ); let mut foreground = InfiniteScrolledMap::new( - tiled.background(Priority::P0), + tiled.background(Priority::P0, RegularBackgroundSize::Background32x32), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( diff --git a/examples/the-purple-night/src/main.rs b/examples/the-purple-night/src/main.rs index 930f7cd9..6bb170ab 100644 --- a/examples/the-purple-night/src/main.rs +++ b/examples/the-purple-night/src/main.rs @@ -12,7 +12,10 @@ use alloc::{boxed::Box, vec::Vec}; use agb::{ display::{ object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap}, - tiled::{InfiniteScrolledMap, TileFormat, TileSet, TileSetting, VRamManager}, + tiled::{ + InfiniteScrolledMap, RegularBackgroundSize, TileFormat, TileSet, TileSetting, + VRamManager, + }, Priority, HEIGHT, WIDTH, }, fixnum::{FixedNum, Rect, Vector2D}, @@ -2221,7 +2224,7 @@ fn game_with_level(gba: &mut agb::Gba) { let object = gba.display.object.get(); let backdrop = InfiniteScrolledMap::new( - background.background(Priority::P2), + background.background(Priority::P2, RegularBackgroundSize::Background32x32), Box::new(|pos| { ( &tileset, @@ -2235,7 +2238,7 @@ fn game_with_level(gba: &mut agb::Gba) { ); let foreground = InfiniteScrolledMap::new( - background.background(Priority::P0), + background.background(Priority::P0, RegularBackgroundSize::Background32x32), Box::new(|pos| { ( &tileset, @@ -2249,7 +2252,7 @@ fn game_with_level(gba: &mut agb::Gba) { ); let clouds = InfiniteScrolledMap::new( - background.background(Priority::P3), + background.background(Priority::P3, RegularBackgroundSize::Background32x32), Box::new(|pos| { ( &tileset, From 4d3d2acd3bb651f567ad409e9225da30aca43d9c Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 22:55:50 +0100 Subject: [PATCH 10/16] This should always be 32 --- agb/src/display/tiled/infinite_scrolled_map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index e2e90aff..1af77c99 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -151,8 +151,8 @@ impl<'a> InfiniteScrolledMap<'a> { // calculate which direction we need to update let direction = difference.y.signum(); - // either need to update width - 2 or width - 1 tiles depending on whether the x coordinate is a perfect multiple - let x_tiles_to_update: i32 = size.width() as i32; + // either need to update 30 or 31 tiles depending on whether the x coordinate is a perfect multiple + let x_tiles_to_update: i32 = 32; let line_to_update = if direction < 0 { // moving up so need to update the top From 817e1e305e6eb76f830b57b8e1574fc029af2995 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:00:31 +0100 Subject: [PATCH 11/16] Add rem_euclid_*_px for pixel calculations --- agb/src/display/tiled/infinite_scrolled_map.rs | 4 ++-- agb/src/display/tiled/mod.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index 1af77c99..cef44351 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -190,8 +190,8 @@ impl<'a> InfiniteScrolledMap<'a> { let current_scroll = self.map.scroll_pos(); let new_scroll = ( - size.rem_euclid_width(current_scroll.x as i32 + difference.x) as u16, - size.rem_euclid_height(current_scroll.y as i32 + difference.y) as u16, + size.rem_euclid_width_px(current_scroll.x as i32 + difference.x) as u16, + size.rem_euclid_height_px(current_scroll.y as i32 + difference.y) as u16, ) .into(); diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 2fabce54..12d8624c 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -55,6 +55,14 @@ impl RegularBackgroundSize { pub(crate) fn rem_euclid_height(&self, y: i32) -> u32 { (y as u32) & (self.height() - 1) } + + pub(crate) fn rem_euclid_width_px(&self, x: i32) -> u32 { + (x as u32) & (self.width() * 8 - 1) + } + + pub(crate) fn rem_euclid_height_px(&self, y: i32) -> u32 { + (y as u32) & (self.height() * 8 - 1) + } } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] From ae65af4ca254ab14fea3b9ee3b5a4984332cb0aa Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:03:23 +0100 Subject: [PATCH 12/16] Make rem_euclid_* return a u16 --- .../display/tiled/infinite_scrolled_map.rs | 12 +++++----- agb/src/display/tiled/mod.rs | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index cef44351..6be831da 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -63,8 +63,8 @@ impl<'a> InfiniteScrolledMap<'a> { let offset = self.current_pos - (x_start * 8, y_start * 8).into(); let offset_scroll = ( - self.map.size().rem_euclid_width(offset.x) as u16, - self.map.size().rem_euclid_height(offset.y) as u16, + self.map.size().rem_euclid_width(offset.x), + self.map.size().rem_euclid_height(offset.y), ) .into(); @@ -179,8 +179,8 @@ impl<'a> InfiniteScrolledMap<'a> { self.map.set_tile( vram, ( - size.rem_euclid_width(tile_x - self.offset.x) as u16, - size.rem_euclid_height(tile_y - self.offset.y) as u16, + size.rem_euclid_width(tile_x - self.offset.x), + size.rem_euclid_height(tile_y - self.offset.y), ) .into(), tileset, @@ -190,8 +190,8 @@ impl<'a> InfiniteScrolledMap<'a> { let current_scroll = self.map.scroll_pos(); let new_scroll = ( - size.rem_euclid_width_px(current_scroll.x as i32 + difference.x) as u16, - size.rem_euclid_height_px(current_scroll.y as i32 + difference.y) as u16, + size.rem_euclid_width_px(current_scroll.x as i32 + difference.x), + size.rem_euclid_height_px(current_scroll.y as i32 + difference.y), ) .into(); diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 12d8624c..b09f3e6e 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -48,20 +48,20 @@ impl RegularBackgroundSize { (self.width() * self.height()) as usize } - pub(crate) fn rem_euclid_width(&self, x: i32) -> u32 { - (x as u32) & (self.width() - 1) + pub(crate) fn rem_euclid_width(&self, x: i32) -> u16 { + ((x as u32) & (self.width() - 1)) as u16 } - pub(crate) fn rem_euclid_height(&self, y: i32) -> u32 { - (y as u32) & (self.height() - 1) + pub(crate) fn rem_euclid_height(&self, y: i32) -> u16 { + ((y as u32) & (self.height() - 1)) as u16 } - pub(crate) fn rem_euclid_width_px(&self, x: i32) -> u32 { - (x as u32) & (self.width() * 8 - 1) + pub(crate) fn rem_euclid_width_px(&self, x: i32) -> u16 { + ((x as u32) & (self.width() * 8 - 1)) as u16 } - pub(crate) fn rem_euclid_height_px(&self, y: i32) -> u32 { - (y as u32) & (self.height() * 8 - 1) + pub(crate) fn rem_euclid_height_px(&self, y: i32) -> u16 { + ((y as u32) & (self.height() * 8 - 1)) as u16 } } @@ -127,8 +127,11 @@ mod test { assert_eq!(size.rem_euclid_width(3 + width), 3); assert_eq!(size.rem_euclid_width(7 + width * 9), 7); - assert_eq!(size.rem_euclid_width(-8), size.width() - 8); - assert_eq!(size.rem_euclid_width(-17 - width * 8), size.width() - 17); + assert_eq!(size.rem_euclid_width(-8), (size.width() - 8) as u16); + assert_eq!( + size.rem_euclid_width(-17 - width * 8), + (size.width() - 17) as u16 + ); } } } From be650779e645913f37105528179a1b13bd0a7793 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:23:25 +0100 Subject: [PATCH 13/16] Correctly allocate screenblocks --- agb/src/display/tiled/map.rs | 15 ++++++ agb/src/display/tiled/mod.rs | 4 ++ agb/src/display/tiled/tiled0.rs | 47 ++++++++++++++++--- .../the-hat-chooses-the-wizard/src/main.rs | 4 +- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index c42cbeda..568091f6 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -164,7 +164,10 @@ impl RegularMap { pub struct MapLoan<'a, T> { map: T, background_id: u8, + screenblock_id: u8, + screenblock_length: u8, regular_map_list: &'a RefCell>, + screenblock_list: &'a RefCell>, } impl<'a, T> Deref for MapLoan<'a, T> { @@ -185,12 +188,18 @@ impl<'a, T> MapLoan<'a, T> { pub(crate) fn new( map: T, background_id: u8, + screenblock_id: u8, + screenblock_length: u8, regular_map_list: &'a RefCell>, + screenblock_list: &'a RefCell>, ) -> Self { MapLoan { map, background_id, + screenblock_id, + screenblock_length, regular_map_list, + screenblock_list, } } } @@ -200,5 +209,11 @@ impl<'a, T> Drop for MapLoan<'a, T> { self.regular_map_list .borrow_mut() .set(self.background_id as usize, false); + + let mut screenblock_list = self.screenblock_list.borrow_mut(); + + for i in self.screenblock_id..self.screenblock_id + self.screenblock_length { + screenblock_list.set(i as usize, false); + } } } diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index b09f3e6e..5a53cad6 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -48,6 +48,10 @@ impl RegularBackgroundSize { (self.width() * self.height()) as usize } + pub(crate) fn num_screen_blocks(&self) -> usize { + self.num_tiles() / (32 * 32) + } + pub(crate) fn rem_euclid_width(&self, x: i32) -> u16 { ((x as u32) & (self.width() - 1)) as u16 } diff --git a/agb/src/display/tiled/tiled0.rs b/agb/src/display/tiled/tiled0.rs index f6c20db8..fbc523e2 100644 --- a/agb/src/display/tiled/tiled0.rs +++ b/agb/src/display/tiled/tiled0.rs @@ -9,6 +9,7 @@ use super::{MapLoan, RegularBackgroundSize, RegularMap}; pub struct Tiled0 { regular: RefCell>, + screenblocks: RefCell>, } impl Tiled0 { @@ -17,6 +18,7 @@ impl Tiled0 { Self { regular: Default::default(), + screenblocks: Default::default(), } } @@ -31,15 +33,46 @@ impl Tiled0 { panic!("can only have 4 active backgrounds"); } - let bg = RegularMap::new( - new_background as u8, - (new_background + 16) as u8, - priority, - size, - ); + let num_screenblocks = size.num_screen_blocks(); + let mut screenblocks = self.screenblocks.borrow_mut(); + + let screenblock = find_screenblock_gap(&screenblocks, num_screenblocks); + for id in screenblock..(screenblock + num_screenblocks) { + screenblocks.set(id, true); + } + + let bg = RegularMap::new(new_background as u8, screenblock as u8 + 16, priority, size); regular.set(new_background, true); - MapLoan::new(bg, new_background as u8, &self.regular) + MapLoan::new( + bg, + new_background as u8, + screenblock as u8, + num_screenblocks as u8, + &self.regular, + &self.screenblocks, + ) } } + +fn find_screenblock_gap(screenblocks: &Bitarray<1>, gap: usize) -> usize { + let mut candidate = 0; + + 'outer: while candidate < 16 - gap { + let starting_point = candidate; + for attempt in starting_point..(starting_point + gap) { + if screenblocks.get(attempt) == Some(true) { + candidate = attempt + 1; + continue 'outer; + } + } + + return candidate; + } + + panic!( + "Failed to find screenblock gap of at least {} elements", + gap + ); +} diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 57ab5ec2..cf5524dd 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -846,7 +846,7 @@ fn main(mut agb: agb::Gba) -> ! { let map_current_level = current_level; let mut background = InfiniteScrolledMap::new( - tiled.background(Priority::P2, RegularBackgroundSize::Background32x32), + tiled.background(Priority::P2, RegularBackgroundSize::Background64x64), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( @@ -861,7 +861,7 @@ fn main(mut agb: agb::Gba) -> ! { }), ); let mut foreground = InfiniteScrolledMap::new( - tiled.background(Priority::P0, RegularBackgroundSize::Background32x32), + tiled.background(Priority::P0, RegularBackgroundSize::Background64x64), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( From 5211781429a7c58e30c17688fd5111d770066b87 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:23:55 +0100 Subject: [PATCH 14/16] Rename rem_euclid functions --- agb/src/display/tiled/mod.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 5a53cad6..3abc9cfa 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -52,19 +52,19 @@ impl RegularBackgroundSize { self.num_tiles() / (32 * 32) } - pub(crate) fn rem_euclid_width(&self, x: i32) -> u16 { + pub(crate) fn tile_pos_x(&self, x: i32) -> u16 { ((x as u32) & (self.width() - 1)) as u16 } - pub(crate) fn rem_euclid_height(&self, y: i32) -> u16 { + pub(crate) fn tile_pos_y(&self, y: i32) -> u16 { ((y as u32) & (self.height() - 1)) as u16 } - pub(crate) fn rem_euclid_width_px(&self, x: i32) -> u16 { + pub(crate) fn px_offset_x(&self, x: i32) -> u16 { ((x as u32) & (self.width() * 8 - 1)) as u16 } - pub(crate) fn rem_euclid_height_px(&self, y: i32) -> u16 { + pub(crate) fn px_offset_y(&self, y: i32) -> u16 { ((y as u32) & (self.height() * 8 - 1)) as u16 } } @@ -127,15 +127,12 @@ mod test { for size in sizes.iter() { let width = size.width() as i32; - assert_eq!(size.rem_euclid_width(8), 8); - assert_eq!(size.rem_euclid_width(3 + width), 3); - assert_eq!(size.rem_euclid_width(7 + width * 9), 7); + assert_eq!(size.tile_pos_x(8), 8); + assert_eq!(size.tile_pos_x(3 + width), 3); + assert_eq!(size.tile_pos_x(7 + width * 9), 7); - assert_eq!(size.rem_euclid_width(-8), (size.width() - 8) as u16); - assert_eq!( - size.rem_euclid_width(-17 - width * 8), - (size.width() - 17) as u16 - ); + assert_eq!(size.tile_pos_x(-8), (size.width() - 8) as u16); + assert_eq!(size.tile_pos_x(-17 - width * 8), (size.width() - 17) as u16); } } } From 2853d34b30ca006d9460016ae7e373ce420613bd Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:42:11 +0100 Subject: [PATCH 15/16] Correctly render different sized backgrounds and make HTTW have pointlessly different sized backgrounds to test it properly --- agb/src/display/tiled/infinite_scrolled_map.rs | 12 ++++++------ agb/src/display/tiled/map.rs | 2 +- agb/src/display/tiled/mod.rs | 12 ++++++++++++ examples/the-hat-chooses-the-wizard/src/main.rs | 4 ++-- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/agb/src/display/tiled/infinite_scrolled_map.rs b/agb/src/display/tiled/infinite_scrolled_map.rs index 6be831da..41dc67d7 100644 --- a/agb/src/display/tiled/infinite_scrolled_map.rs +++ b/agb/src/display/tiled/infinite_scrolled_map.rs @@ -63,8 +63,8 @@ impl<'a> InfiniteScrolledMap<'a> { let offset = self.current_pos - (x_start * 8, y_start * 8).into(); let offset_scroll = ( - self.map.size().rem_euclid_width(offset.x), - self.map.size().rem_euclid_height(offset.y), + self.map.size().tile_pos_x(offset.x), + self.map.size().tile_pos_y(offset.y), ) .into(); @@ -179,8 +179,8 @@ impl<'a> InfiniteScrolledMap<'a> { self.map.set_tile( vram, ( - size.rem_euclid_width(tile_x - self.offset.x), - size.rem_euclid_height(tile_y - self.offset.y), + size.tile_pos_x(tile_x - self.offset.x), + size.tile_pos_y(tile_y - self.offset.y), ) .into(), tileset, @@ -190,8 +190,8 @@ impl<'a> InfiniteScrolledMap<'a> { let current_scroll = self.map.scroll_pos(); let new_scroll = ( - size.rem_euclid_width_px(current_scroll.x as i32 + difference.x), - size.rem_euclid_height_px(current_scroll.y as i32 + difference.y), + size.px_offset_x(current_scroll.x as i32 + difference.x), + size.px_offset_y(current_scroll.y as i32 + difference.y), ) .into(); diff --git a/agb/src/display/tiled/map.rs b/agb/src/display/tiled/map.rs index 568091f6..34cdc77a 100644 --- a/agb/src/display/tiled/map.rs +++ b/agb/src/display/tiled/map.rs @@ -56,7 +56,7 @@ impl RegularMap { tileset: &TileSet<'_>, tile_setting: TileSetting, ) { - let pos = (pos.x + pos.y * self.size.width() as u16) as usize; + let pos = self.size.gba_offset(pos); let old_tile = self.tiles[pos]; if old_tile != Tile::default() { diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 3abc9cfa..68ae2e95 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -3,6 +3,7 @@ mod map; mod tiled0; mod vram_manager; +use agb_fixnum::Vector2D; pub use infinite_scrolled_map::{InfiniteScrolledMap, PartialUpdateStatus}; pub use map::{MapLoan, RegularMap}; pub use tiled0::Tiled0; @@ -52,6 +53,17 @@ impl RegularBackgroundSize { self.num_tiles() / (32 * 32) } + pub(crate) fn gba_offset(&self, pos: Vector2D) -> usize { + let x_mod = pos.x & (self.width() as u16 - 1); + let y_mod = pos.y & (self.height() as u16 - 1); + + let screenblock = (x_mod / 32) + (y_mod / 32) * (self.width() as u16 / 32); + + let pos = screenblock * 32 * 32 + (x_mod % 32 + 32 * (y_mod % 32)); + + pos as usize + } + pub(crate) fn tile_pos_x(&self, x: i32) -> u16 { ((x as u32) & (self.width() - 1)) as u16 } diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index cf5524dd..578fc7dc 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -846,7 +846,7 @@ fn main(mut agb: agb::Gba) -> ! { let map_current_level = current_level; let mut background = InfiniteScrolledMap::new( - tiled.background(Priority::P2, RegularBackgroundSize::Background64x64), + tiled.background(Priority::P2, RegularBackgroundSize::Background32x64), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( @@ -861,7 +861,7 @@ fn main(mut agb: agb::Gba) -> ! { }), ); let mut foreground = InfiniteScrolledMap::new( - tiled.background(Priority::P0, RegularBackgroundSize::Background64x64), + tiled.background(Priority::P0, RegularBackgroundSize::Background64x32), Box::new(|pos: Vector2D| { let level = &map_tiles::LEVELS[map_current_level as usize]; ( From 3b9909ad5b92799593fb050421347195959f416c Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 3 May 2022 23:43:08 +0100 Subject: [PATCH 16/16] Add comment about how silly the implementation of gba_offset is --- agb/src/display/tiled/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agb/src/display/tiled/mod.rs b/agb/src/display/tiled/mod.rs index 68ae2e95..15907798 100644 --- a/agb/src/display/tiled/mod.rs +++ b/agb/src/display/tiled/mod.rs @@ -53,6 +53,8 @@ impl RegularBackgroundSize { self.num_tiles() / (32 * 32) } + // This is hilariously complicated due to how the GBA stores the background screenblocks. + // See https://www.coranac.com/tonc/text/regbg.htm#sec-map for an explanation pub(crate) fn gba_offset(&self, pos: Vector2D) -> usize { let x_mod = pos.x & (self.width() as u16 - 1); let y_mod = pos.y & (self.height() as u16 - 1);