From a7ac5d508a50d89f78fa0c7a824556c482d770e1 Mon Sep 17 00:00:00 2001 From: Lokathor Date: Mon, 24 Oct 2022 14:38:38 -0600 Subject: [PATCH] fix affine stuff. --- CHANGELOG.md | 4 + Cargo.toml | 4 + examples/hello.rs | 2 +- src/fixed.rs | 3 +- src/mmio.rs | 195 ++++++++++++++++++++++++++++++---------------- 5 files changed, 139 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bad50a..622ff30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +* **0.10.0:** + * **Breaking:** Cleaned up the screenblock interface. Because they're in VRAM, + they can't use `u8` access like they were defined to use before. Now the + types use `u8x2`, similar to the Mode 4 bitmap. * **0.9.3:** * Added `as_u32_slice` and `as_u16_slice` to `Align4`. * *Removed* the requirement for inputs to `include_aligned_bytes!` to be a diff --git a/Cargo.toml b/Cargo.toml index 5554a7a..04a807b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,10 @@ version = "0.9.3" edition = "2021" license = "Zlib OR Apache-2.0 OR MIT" +[features] +default = ["track_caller"] +track_caller = [] + [dependencies] bitfrob = "0.2.3" voladdress = { version = "1.2.1", features = ["experimental_volregion"] } diff --git a/examples/hello.rs b/examples/hello.rs index 9a0a1e4..11a00c3 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -82,7 +82,7 @@ extern "C" fn main() -> ! { { // get the the tilemap copied into place let tsb = TextScreenblockAddress::new(31); - tsb.write_words(&TILE_LAYOUT); + tsb.write_word_array(&TILE_LAYOUT); } { diff --git a/src/fixed.rs b/src/fixed.rs index 04e5dd4..66d1c05 100644 --- a/src/fixed.rs +++ b/src/fixed.rs @@ -8,7 +8,8 @@ pub type i16fx8 = Fixed; /// `i16` with 14 bits of fixed-point fraction. /// -/// This is used by the [`ArcTan`] and [`ArcTan2`] BIOS functions. +/// This is used by the [`ArcTan`](crate::bios::ArcTan) and +/// [`ArcTan2`](crate::bios::ArcTan2) BIOS functions. #[allow(non_camel_case_types)] pub type i16fx14 = Fixed; diff --git a/src/mmio.rs b/src/mmio.rs index ee10342..9a1c878 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -214,6 +214,17 @@ def_mmio!(0x0500_2000 = OBJ_PALETTE: VolBlock; "Object t // Video RAM (VRAM) +/// The VRAM offset per screenblock index. +/// +/// This is the same for all background types and sizes. +pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; + +/// The size of the background tile region of VRAM. +/// +/// Background tile index use can cross between charblocks, but not past the end +/// of BG tile memory into OBJ tile memory. +pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024; + def_mmio!(0x0600_0000 = CHARBLOCK0_4BPP: VolBlock; "Charblock 0, 4bpp view (512 tiles)."); def_mmio!(0x0600_4000 = CHARBLOCK1_4BPP: VolBlock; "Charblock 1, 4bpp view (512 tiles)."); def_mmio!(0x0600_8000 = CHARBLOCK2_4BPP: VolBlock; "Charblock 2, 4bpp view (512 tiles)."); @@ -228,101 +239,151 @@ def_mmio!(0x0600_C000 = CHARBLOCK3_8BPP: VolBlock; "Char #[must_use] const fn screenblock_addr(index: usize) -> usize { // The VRAM offset per screenblock isn't affected by the screenblock's size, it's always 2k - 0x0600_0000 + index * 2_048 + 0x0600_0000 + index * SCREENBLOCK_INDEX_OFFSET } -macro_rules! make_me_a_screenblock_addr { - ( $(#[$name_meta:meta])* $name:ident($t:ty), $(#[$size_meta:meta])* size: $size:literal, $(#[$index_meta:meta])* max_index: $max_index:literal) => { +/// Text mode screenblock address. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct TextScreenblockAddress { + block: VolBlock, +} +impl TextScreenblockAddress { + const WORD_COUNT: usize = (32 * 32 * size_of::())/4; + + #[inline] + #[must_use] + #[cfg_attr(feature="track_caller", track_caller)] + pub const fn new(index: usize) -> Self { + assert!(index <= 31); + Self { block: unsafe { VolBlock::new(screenblock_addr(index)) } } + } + + #[inline] + #[must_use] + pub const fn as_vol_block(self) -> VolBlock { + self.block + } + + #[inline] + #[must_use] + #[cfg_attr(feature="track_caller", track_caller)] + pub const fn row_col(self, row: usize, col: usize) -> VolAddress { + assert!(row < 32, "`row` must be less than 32"); + assert!(col < 32, "`col` must be less than 32"); + self.block.index(row * 32 + col) + } + + #[inline] + pub fn write_word_array(self, words: &[u32; Self::WORD_COUNT]) { + use crate::prelude::__aeabi_memcpy4; + let dest: *mut u32 = self.block.as_ptr() as *mut u32; + let src: *const u32 = words.as_ptr(); + let byte_count = size_of::<[u32; Self::WORD_COUNT]>(); + unsafe { __aeabi_memcpy4(dest.cast(), src.cast(), byte_count) }; + } + + #[inline] + #[cfg_attr(feature="track_caller", track_caller)] + pub fn write_word_slice(self, words: &[u32]) { + assert_eq!(words.len(), Self::WORD_COUNT); + self.write_word_array(unsafe { &*words.as_ptr().cast::<[u32; Self::WORD_COUNT]>() }) + } +} + +macro_rules! make_affine_screenblock_address_type { + ($(#[$name_meta:meta])* $name:ident, size: $size:literal) => { + $(#[$name_meta])* #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] - $(#[$name_meta])* pub struct $name { - block: VolBlock<$t, Safe, Safe, {$size*$size}>, + block: VolBlock, } impl $name { + pub const BG_SIZE: usize = $size; + pub const WIDTH: usize = (16 << Self::BG_SIZE) / 2; + pub const HEIGHT: usize = 16 << Self::BG_SIZE; + pub const ENTRIES: usize = Self::WIDTH * Self::HEIGHT; + pub const BYTES: usize = Self::ENTRIES * size_of::(); + pub const WORD_COUNT: usize = { + assert!(Self::BYTES % 4 == 0); + Self::BYTES/4 + }; + pub const MAX_INDEX: usize = { + let mut i = 0; + while ((i+1) * SCREENBLOCK_INDEX_OFFSET) + (Self::BYTES) <= BG_TILE_REGION_SIZE { + i += 1; + } + if i > 31 { + 31 + } else { + i + } + }; + #[inline] #[must_use] - $(#[$index_meta])* + #[cfg_attr(feature="track_caller", track_caller)] pub const fn new(index: usize) -> Self { - assert!(index <= $max_index); + assert!(index <= Self::MAX_INDEX); Self { block: unsafe { VolBlock::new(screenblock_addr(index)) } } } - - #[inline] - #[must_use] - pub const fn as_usize(self) -> usize { - self.block.as_usize() - } #[inline] #[must_use] - $(#[$size_meta])* - pub const fn row_col(self, row: usize, col: usize) -> VolAddress<$t, Safe, Safe> { - assert!(row < $size, concat!("`row` must be less than ", $size)); - assert!(col < $size, concat!("`col` must be less than ", $size)); - self.block.index(row * $size + col) + pub const fn as_vol_block(self) -> VolBlock { + self.block } - - const WORD_COUNT: usize = (size_of::<$t>() * $size * $size)/4; - const _DEBUG_CHECK: () = { - assert!((size_of::<$t>() * $size * $size) % 4 == 0); - () - }; - - /// Overwrites the entire screenblock with the data provided. - pub fn write_words(self, words: &[u32; Self::WORD_COUNT]) { + + #[inline] + #[must_use] + #[cfg_attr(feature="track_caller", track_caller)] + pub const fn row_col(self, row: usize, col: usize) -> VolAddress { + assert!(row < Self::HEIGHT, "`row` out of bounds."); + assert!(col < Self::WIDTH, "`col` out of bounds."); + self.block.index(row * 32 + col) + } + + #[inline] + pub fn write_word_array(self, words: &[u32; Self::WORD_COUNT]) { use crate::prelude::__aeabi_memcpy4; let dest: *mut u32 = self.block.as_ptr() as *mut u32; let src: *const u32 = words.as_ptr(); let byte_count = size_of::<[u32; Self::WORD_COUNT]>(); unsafe { __aeabi_memcpy4(dest.cast(), src.cast(), byte_count) }; } + + #[inline] + #[cfg_attr(feature="track_caller", track_caller)] + pub fn write_word_slice(self, words: &[u32]) { + assert_eq!(words.len(), Self::WORD_COUNT); + self.write_word_array(unsafe { &*words.as_ptr().cast::<[u32; Self::WORD_COUNT]>() }) + } } } } - -make_me_a_screenblock_addr!( - /// Screenblock address for text mode backgrounds (32x32). - TextScreenblockAddress(TextEntry), - /// Size: 32x32 - size: 32, - /// Max Index: 31 - max_index: 31 +make_affine_screenblock_address_type!( + /// Affine mode screenblock address for a size 0 background. + Affine0ScreenblockAddress, + size: 0 ); -make_me_a_screenblock_addr!( - /// Screenblock address for size 0 affine mode backgrounds (16x16). - AffineScreenBlock0Address(u8), - /// Size: 16x16 - size: 16, - /// Max Index: 31 - max_index: 31 +make_affine_screenblock_address_type!( + /// Affine mode screenblock address for a size 1 background. + Affine1ScreenblockAddress, + size: 1 ); -make_me_a_screenblock_addr!( - /// Screenblock address for size 1 affine mode backgrounds (32x32). - AffineScreenBlock1Address(u8), - /// Size: 32x32 - size: 32, - /// Max Index: 31 - max_index: 31 +make_affine_screenblock_address_type!( + /// Affine mode screenblock address for a size 2 background. + Affine2ScreenblockAddress, + size: 2 ); -make_me_a_screenblock_addr!( - /// Screenblock address for size 2 affine mode backgrounds (64x64). - AffineScreenBlock2Address(u8), - /// Size: 64x64 - size: 64, - /// Max Index: 29 - max_index: 29 -); -make_me_a_screenblock_addr!( - /// Screenblock address for size 3 affine mode backgrounds (128x128). - AffineScreenBlock3Address(u8), - /// Size: 128x128 - size: 128, - /// Max Index: 23 - max_index: 23 +make_affine_screenblock_address_type!( + /// Affine mode screenblock address for a size 3 background. + Affine3ScreenblockAddress, + size: 3 ); -/// Video mode 3 has a single full resolution bitmap +/// Accesses VRAM as the Video Mode 3 single bitmap. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VideoMode3Bitmap; impl VideoMode3Bitmap { @@ -351,7 +412,7 @@ impl VideoMode3Bitmap { } } -/// Video mode 4 has two 8bpp indexmaps. +/// Accesses VRAM as a Video Mode 4 bitmap frame. /// /// Because VRAM can't be written with less than 2 bytes at a time, the /// scanlines here use `u8x2` to represent pixels pairs so that all the writes @@ -374,7 +435,7 @@ impl VideoMode4Frame { } } -/// Video mode 5 has two reduced-resolution bitmaps. +/// Accesses VRAM as a Video Mode 5 bitmap frame. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VideoMode5Frame(usize); impl VideoMode5Frame {