diff --git a/Cargo.toml b/Cargo.toml index bb3d4ac..2801cdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gba" description = "A crate (and book) for making GBA games with Rust." -version = "0.3.2" +version = "0.4.0" authors = ["Lokathor ", "Thomas Winwood "] repository = "https://github.com/rust-console/gba" readme = "README.md" @@ -9,10 +9,11 @@ keywords = ["gba"] edition = "2018" license = "Apache-2.0" -#publish = false +publish = false [dependencies] typenum = "1.10" +voladdress = "0.2" gba-proc-macro = "0.5" [profile.release] diff --git a/book/src-bak/03-volatile_destination.md b/book/src-bak/03-volatile_destination.md index ce50a98..18f8fdb 100644 --- a/book/src-bak/03-volatile_destination.md +++ b/book/src-bak/03-volatile_destination.md @@ -146,7 +146,7 @@ So, again using the `hello_magic` example, we had And instead we could declare ```rust -const MAGIC_LOCATION: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0000) }; +const MAGIC_LOCATION: VolAddress = unsafe { VolAddress::new(0x400_0000) }; ``` ### Using A VolAddress Value @@ -300,7 +300,7 @@ Now we can have something like: ```rust const OTHER_MAGIC: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked( - VolAddress::new_unchecked(0x600_0000), + VolAddress::new(0x600_0000), 240 * 160 ) }; diff --git a/src/base.rs b/src/base.rs index 5bbf7d2..ed07b9e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,7 +1,7 @@ -//! Holds fundamental types/ops which the rest of the crate it built on. +//! Holds fundamental types/ops which the rest of the crate is built on. +//! +//! These things should probably, in time, be extracted out into their own +//! crate (or convert to using a robust existing crate). pub mod fixed_point; //pub(crate) use self::fixed_point::*; - -pub mod volatile; -pub(crate) use self::volatile::*; diff --git a/src/base/volatile.rs b/src/base/volatile.rs deleted file mode 100644 index 5d44beb..0000000 --- a/src/base/volatile.rs +++ /dev/null @@ -1,298 +0,0 @@ -//! Holds types for correct handling of volatile memory. - -use core::{cmp::Ordering, iter::FusedIterator, marker::PhantomData, num::NonZeroUsize}; - -// TODO: striding block/iter - -/// Abstracts the use of a volatile hardware address. -/// -/// If you're trying to do anything other than abstract a volatile hardware -/// device then you _do not want to use this type_. Use one of the many other -/// smart pointer types. -/// -/// A volatile address doesn't store a value in the normal way: It maps to some -/// real hardware _other than_ RAM, and that hardware might have any sort of -/// strange rules. The specifics of reading and writing depend on the hardware -/// being mapped. For example, a particular address might be read only (ignoring -/// writes), write only (returning some arbitrary value if you read it), -/// "normal" read write (where you read back what you wrote), or some complex -/// read-write situation where writes have an effect but you _don't_ read back -/// what you wrote. -/// -/// As you imagine it can be very unsafe. The design of this type is set up so -/// that _creation_ is unsafe, and _use_ is safe. This gives an optimal -/// experience, since you'll use memory locations a lot more often than you try -/// to name them, on average. -/// -/// `VolAddress` is _not_ a thread safe type. If your device is multi-threaded -/// then you must arrange for synchronization in some other way. A `VolAddress` -/// _can_ be used to share data between an interrupt running on a core and a -/// thread running on that core as long as all access of that location is -/// volatile (if you're using the `asm!` macro add the "volatile" option, if -/// you're linking in ASM with the linker that's effectively volatile since the -/// compiler doesn't get a chance to mess with it). -/// -/// # Safety -/// -/// In order for values of this type to operate correctly they must follow quite -/// a few safety limits: -/// -/// * The declared address must be non-null (it uses the `NonNull` optimization -/// for better iteration results). This shouldn't be a big problem, since -/// hardware can't really live at the null address. -/// * The declared address must be aligned for the declared type of `T`. -/// * The declared address must _always_ read as something that's a valid bit -/// pattern for `T`. Don't pick any enums or things like that if your hardware -/// doesn't back it up. If there's _any_ doubt at all, you must instead read -/// or write an unsigned int of the correct bit size and then parse the bits -/// by hand. -/// * The declared address must be a part of the address space that Rust's -/// allocator and/or stack frames will never use. If you're not sure, please -/// re-read the hardware specs of your device and its memory map until you -/// know. -/// -/// The exact points of UB are if the address is ever 0, or if you ever `read` -/// or `write` with the invalid pointer. For example, if you offset to some -/// crazy (non-zero) value and then never use it that won't be an immediate -/// trigger of UB. -#[derive(Debug)] -#[repr(transparent)] -pub struct VolAddress { - address: NonZeroUsize, - marker: PhantomData<*mut T>, -} -// Note(Lokathor): We have to hand implement all these traits because if we use -// `derive` then they only get derived if the inner `T` has the trait. However, -// since we're acting like a pointer to `T`, the capability we offer isn't -// affected by whatever type `T` ends up being. -impl Clone for VolAddress { - fn clone(&self) -> Self { - *self - } -} -impl Copy for VolAddress {} -impl PartialEq for VolAddress { - fn eq(&self, other: &Self) -> bool { - self.address == other.address - } -} -impl Eq for VolAddress {} -impl PartialOrd for VolAddress { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.address.cmp(&other.address)) - } -} -impl Ord for VolAddress { - fn cmp(&self, other: &Self) -> Ordering { - self.address.cmp(&other.address) - } -} - -impl VolAddress { - /// Constructs a new address. - /// - /// # Safety - /// - /// You must follow the standard safety rules as outlined in the type docs. - pub const unsafe fn new_unchecked(address: usize) -> Self { - VolAddress { - address: NonZeroUsize::new_unchecked(address), - marker: PhantomData, - } - } - - /// Casts the type of `T` into type `Z`. - /// - /// # Safety - /// - /// You must follow the standard safety rules as outlined in the type docs. - pub const unsafe fn cast(self) -> VolAddress { - VolAddress { - address: self.address, - marker: PhantomData, - } - } - - /// Offsets the address by `offset` slots (like `pointer::wrapping_offset`). - /// - /// # Safety - /// - /// You must follow the standard safety rules as outlined in the type docs. - pub const unsafe fn offset(self, offset: isize) -> Self { - VolAddress { - address: NonZeroUsize::new_unchecked(self.address.get().wrapping_add(offset as usize * core::mem::size_of::())), - marker: PhantomData, - } - } - - /// Checks that the current target type of this address is aligned at this - /// address value. - /// - /// Technically it's a safety violation to even make a `VolAddress` that isn't - /// aligned. However, I know you're gonna try doing the bad thing, and it's - /// better to give you a chance to call `is_aligned` and potentially back off - /// from the operation or throw a `debug_assert!` or something instead of - /// triggering UB. Eventually this will be `const fn`, which will potentially - /// let you spot errors without even having to run your program. - pub const fn is_aligned(self) -> bool { - self.address.get() % core::mem::align_of::() == 0 - } - - /// Makes an iterator starting here across the given number of slots. - /// - /// # Safety - /// - /// The normal safety rules must be correct for each address iterated over. - pub const unsafe fn iter_slots(self, slots: usize) -> VolAddressIter { - VolAddressIter { vol_address: self, slots } - } - - // non-const and never can be. - - /// Reads a `Copy` value out of the address. - /// - /// The `Copy` bound is actually supposed to be `!Drop`, but rust doesn't - /// allow negative trait bounds. If your type isn't `Copy` you can use the - /// `read_non_copy` fallback to do an unsafe read. - /// - /// That said, I don't think that you legitimately have hardware that maps to - /// a Rust type with a `Drop` impl. If you do please tell me, I'm interested - /// to hear about it. - pub fn read(self) -> T - where - T: Copy, - { - unsafe { (self.address.get() as *mut T).read_volatile() } - } - - /// Reads a value out of the address with no trait bound. - /// - /// # Safety - /// - /// This is _not_ a move, it forms a bit duplicate of the current address - /// value. If `T` has a `Drop` trait that does anything it is up to you to - /// ensure that repeated drops do not cause UB (such as a double free). - pub unsafe fn read_non_copy(self) -> T { - (self.address.get() as *mut T).read_volatile() - } - - /// Writes a value to the address. - /// - /// Semantically, the value is moved into the `VolAddress` and then forgotten, - /// so if `T` has a `Drop` impl then that will never get executed. This is - /// "safe" under Rust's safety rules, but could cause something unintended - /// (eg: a memory leak). - pub fn write(self, val: T) { - unsafe { (self.address.get() as *mut T).write_volatile(val) } - } -} - -/// An iterator that produces a series of `VolAddress` values. -#[derive(Debug)] -pub struct VolAddressIter { - vol_address: VolAddress, - slots: usize, -} -impl Clone for VolAddressIter { - fn clone(&self) -> Self { - VolAddressIter { - vol_address: self.vol_address, - slots: self.slots, - } - } -} -impl PartialEq for VolAddressIter { - fn eq(&self, other: &Self) -> bool { - self.vol_address == other.vol_address && self.slots == other.slots - } -} -impl Eq for VolAddressIter {} -impl Iterator for VolAddressIter { - type Item = VolAddress; - - fn next(&mut self) -> Option { - if self.slots > 0 { - let out = self.vol_address; - unsafe { - self.slots -= 1; - self.vol_address = self.vol_address.offset(1); - } - Some(out) - } else { - None - } - } -} -impl FusedIterator for VolAddressIter {} - -/// This type is like `VolAddress`, but for when you have a block of values all -/// in a row. -/// -/// This is similar to the idea of an array or a slice, but called a "block" -/// because you could _also_ construct a `[VolAddress]`, and we want to avoid -/// any accidental confusion. -#[derive(Debug)] -pub struct VolAddressBlock { - vol_address: VolAddress, - slots: usize, -} -impl Clone for VolAddressBlock { - fn clone(&self) -> Self { - VolAddressBlock { - vol_address: self.vol_address, - slots: self.slots, - } - } -} -impl PartialEq for VolAddressBlock { - fn eq(&self, other: &Self) -> bool { - self.vol_address == other.vol_address && self.slots == other.slots - } -} -impl Eq for VolAddressBlock {} - -impl VolAddressBlock { - /// Constructs a new `VolAddressBlock`. - /// - /// # Safety - /// - /// The given `VolAddress` must be valid when offset by each of `0 .. slots` - pub const unsafe fn new_unchecked(vol_address: VolAddress, slots: usize) -> Self { - VolAddressBlock { vol_address, slots } - } - - /// Gives an iterator over this block's slots. - pub const fn iter(self) -> VolAddressIter { - VolAddressIter { - vol_address: self.vol_address, - slots: self.slots, - } - } - - /// Unchecked indexing into the block. - /// - /// # Safety - /// - /// The slot given must be in bounds. - pub const unsafe fn index_unchecked(self, slot: usize) -> VolAddress { - self.vol_address.offset(slot as isize) - } - - /// Checked "indexing" style access of the block, giving either a `VolAddress` or a panic. - pub fn index(self, slot: usize) -> VolAddress { - if slot < self.slots { - unsafe { self.vol_address.offset(slot as isize) } - } else { - panic!("Index Requested: {} >= Slot Count: {}", slot, self.slots) - } - } - - /// Checked "getting" style access of the block, giving an Option value. - pub fn get(self, slot: usize) -> Option> { - if slot < self.slots { - unsafe { Some(self.vol_address.offset(slot as isize)) } - } else { - None - } - } -} diff --git a/src/io/background.rs b/src/io/background.rs index 09748ea..6b34f2a 100644 --- a/src/io/background.rs +++ b/src/io/background.rs @@ -3,13 +3,13 @@ use super::*; /// BG0 Control. Read/Write. Display Mode 0/1 only. -pub const BG0CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0008) }; +pub const BG0CNT: VolAddress = unsafe { VolAddress::new(0x400_0008) }; /// BG1 Control. Read/Write. Display Mode 0/1 only. -pub const BG1CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000A) }; +pub const BG1CNT: VolAddress = unsafe { VolAddress::new(0x400_000A) }; /// BG2 Control. Read/Write. Display Mode 0/1/2 only. -pub const BG2CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000C) }; +pub const BG2CNT: VolAddress = unsafe { VolAddress::new(0x400_000C) }; /// BG3 Control. Read/Write. Display Mode 0/2 only. -pub const BG3CNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_000E) }; +pub const BG3CNT: VolAddress = unsafe { VolAddress::new(0x400_000E) }; newtype! { /// Allows configuration of a background layer. @@ -66,24 +66,24 @@ pub enum BGSize { } /// BG0 X-Offset. Write only. Text mode only. 9 bits. -pub const BG0HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0010) }; +pub const BG0HOFS: VolAddress = unsafe { VolAddress::new(0x400_0010) }; /// BG0 Y-Offset. Write only. Text mode only. 9 bits. -pub const BG0VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0012) }; +pub const BG0VOFS: VolAddress = unsafe { VolAddress::new(0x400_0012) }; /// BG1 X-Offset. Write only. Text mode only. 9 bits. -pub const BG1HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0014) }; +pub const BG1HOFS: VolAddress = unsafe { VolAddress::new(0x400_0014) }; /// BG1 Y-Offset. Write only. Text mode only. 9 bits. -pub const BG1VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0016) }; +pub const BG1VOFS: VolAddress = unsafe { VolAddress::new(0x400_0016) }; /// BG2 X-Offset. Write only. Text mode only. 9 bits. -pub const BG2HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0018) }; +pub const BG2HOFS: VolAddress = unsafe { VolAddress::new(0x400_0018) }; /// BG2 Y-Offset. Write only. Text mode only. 9 bits. -pub const BG2VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001A) }; +pub const BG2VOFS: VolAddress = unsafe { VolAddress::new(0x400_001A) }; /// BG3 X-Offset. Write only. Text mode only. 9 bits. -pub const BG3HOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001C) }; +pub const BG3HOFS: VolAddress = unsafe { VolAddress::new(0x400_001C) }; /// BG3 Y-Offset. Write only. Text mode only. 9 bits. -pub const BG3VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_001E) }; +pub const BG3VOFS: VolAddress = unsafe { VolAddress::new(0x400_001E) }; // TODO: affine backgrounds // BG2X_L @@ -100,14 +100,14 @@ pub const BG3VOFS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00 // BG3PD // TODO: windowing -// pub const WIN0H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0040) }; -// pub const WIN1H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0042) }; -// pub const WIN0V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0044) }; -// pub const WIN1V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0046) }; -// pub const WININ: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0048) }; -// pub const WINOUT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004A) }; +// pub const WIN0H: VolAddress = unsafe { VolAddress::new(0x400_0040) }; +// pub const WIN1H: VolAddress = unsafe { VolAddress::new(0x400_0042) }; +// pub const WIN0V: VolAddress = unsafe { VolAddress::new(0x400_0044) }; +// pub const WIN1V: VolAddress = unsafe { VolAddress::new(0x400_0046) }; +// pub const WININ: VolAddress = unsafe { VolAddress::new(0x400_0048) }; +// pub const WINOUT: VolAddress = unsafe { VolAddress::new(0x400_004A) }; // TODO: blending -// pub const BLDCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0050) }; -// pub const BLDALPHA: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0052) }; -// pub const BLDY: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0054) }; +// pub const BLDCNT: VolAddress = unsafe { VolAddress::new(0x400_0050) }; +// pub const BLDALPHA: VolAddress = unsafe { VolAddress::new(0x400_0052) }; +// pub const BLDY: VolAddress = unsafe { VolAddress::new(0x400_0054) }; diff --git a/src/io/color_blend.rs b/src/io/color_blend.rs index a1d041f..837c505 100644 --- a/src/io/color_blend.rs +++ b/src/io/color_blend.rs @@ -3,7 +3,7 @@ use super::*; /// Color Special Effects Selection (R/W) -pub const BLDCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0050) }; +pub const BLDCNT: VolAddress = unsafe { VolAddress::new(0x400_0050) }; newtype! { ColorEffectSetting, u16 @@ -37,7 +37,7 @@ newtype_enum! { } /// Alpha Blending Coefficients (R/W) (not W) -pub const BLDALPHA: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0052) }; +pub const BLDALPHA: VolAddress = unsafe { VolAddress::new(0x400_0052) }; newtype! { AlphaBlendingSetting, u16 @@ -52,7 +52,7 @@ impl AlphaBlendingSetting { } /// Brightness (Fade-In/Out) Coefficient (W) (not R/W) -pub const BLDY: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0054) }; +pub const BLDY: VolAddress = unsafe { VolAddress::new(0x400_0054) }; newtype! { BrightnessSetting, u32 diff --git a/src/io/display.rs b/src/io/display.rs index 058143e..deecb88 100644 --- a/src/io/display.rs +++ b/src/io/display.rs @@ -5,7 +5,7 @@ use super::*; /// LCD Control. Read/Write. /// /// The "force vblank" bit is always set when your Rust code first executes. -pub const DISPCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0000) }; +pub const DISPCNT: VolAddress = unsafe { VolAddress::new(0x400_0000) }; newtype!( /// Setting for the display control register. @@ -96,7 +96,7 @@ pub fn display_control() -> DisplayControlSetting { } /// Display Status and IRQ Control. Read/Write. -pub const DISPSTAT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0004) }; +pub const DISPSTAT: VolAddress = unsafe { VolAddress::new(0x400_0004) }; newtype!( /// A newtype over display status and interrupt control values. @@ -122,7 +122,7 @@ impl DisplayStatusSetting { /// Gives the current scanline that the display controller is working on. If /// this is at or above the `VBLANK_SCANLINE` value then the display controller /// is in a "vertical blank" period. -pub const VCOUNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0006) }; +pub const VCOUNT: VolAddress = unsafe { VolAddress::new(0x400_0006) }; /// If the `VCOUNT` register reads equal to or above this then you're in vblank. pub const VBLANK_SCANLINE: u16 = 160; @@ -145,7 +145,7 @@ pub fn spin_until_vdraw() { } /// Global mosaic effect control. Write-only. -pub const MOSAIC: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004C) }; +pub const MOSAIC: VolAddress = unsafe { VolAddress::new(0x400_004C) }; newtype! { /// Allows control of the Mosaic effect. diff --git a/src/io/dma.rs b/src/io/dma.rs index 38f7567..96d9d14 100644 --- a/src/io/dma.rs +++ b/src/io/dma.rs @@ -127,13 +127,13 @@ pub enum DMAStartTiming { pub struct DMA0; impl DMA0 { /// DMA 0 Source Address, read only. - const DMA0SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00B0) }; + const DMA0SAD: VolAddress<*const u32> = unsafe { VolAddress::new(0x400_00B0) }; /// DMA 0 Destination Address, read only. - const DMA0DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00B4) }; + const DMA0DAD: VolAddress<*mut u32> = unsafe { VolAddress::new(0x400_00B4) }; /// DMA 0 Word Count, read only. - const DMA0CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00B8) }; + const DMA0CNT_L: VolAddress = unsafe { VolAddress::new(0x400_00B8) }; /// DMA 0 Control, read/write. - const DMA0CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00BA) }; + const DMA0CNT_H: VolAddress = unsafe { VolAddress::new(0x400_00BA) }; /// Assigns the source register. /// @@ -188,13 +188,13 @@ impl DMA0 { pub struct DMA1; impl DMA1 { /// DMA 1 Source Address, read only. - const DMA1SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00BC) }; + const DMA1SAD: VolAddress<*const u32> = unsafe { VolAddress::new(0x400_00BC) }; /// DMA 1 Destination Address, read only. - const DMA1DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00C0) }; + const DMA1DAD: VolAddress<*mut u32> = unsafe { VolAddress::new(0x400_00C0) }; /// DMA 1 Word Count, read only. - const DMA1CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00C4) }; + const DMA1CNT_L: VolAddress = unsafe { VolAddress::new(0x400_00C4) }; /// DMA 1 Control, read/write. - const DMA1CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00C6) }; + const DMA1CNT_H: VolAddress = unsafe { VolAddress::new(0x400_00C6) }; /// Assigns the source register. /// @@ -249,13 +249,13 @@ impl DMA1 { pub struct DMA2; impl DMA2 { /// DMA 2 Source Address, read only. - const DMA2SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00C8) }; + const DMA2SAD: VolAddress<*const u32> = unsafe { VolAddress::new(0x400_00C8) }; /// DMA 2 Destination Address, read only. - const DMA2DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00CC) }; + const DMA2DAD: VolAddress<*mut u32> = unsafe { VolAddress::new(0x400_00CC) }; /// DMA 2 Word Count, read only. - const DMA2CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00D0) }; + const DMA2CNT_L: VolAddress = unsafe { VolAddress::new(0x400_00D0) }; /// DMA 2 Control, read/write. - const DMA2CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00D2) }; + const DMA2CNT_H: VolAddress = unsafe { VolAddress::new(0x400_00D2) }; /// Assigns the source register. /// @@ -311,13 +311,13 @@ impl DMA2 { pub struct DMA3; impl DMA3 { /// DMA 3 Source Address, read only. - const DMA3SAD: VolAddress<*const u32> = unsafe { VolAddress::new_unchecked(0x400_00D4) }; + const DMA3SAD: VolAddress<*const u32> = unsafe { VolAddress::new(0x400_00D4) }; /// DMA 3 Destination Address, read only. - const DMA3DAD: VolAddress<*mut u32> = unsafe { VolAddress::new_unchecked(0x400_00D8) }; + const DMA3DAD: VolAddress<*mut u32> = unsafe { VolAddress::new(0x400_00D8) }; /// DMA 3 Word Count, read only. - const DMA3CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00DC) }; + const DMA3CNT_L: VolAddress = unsafe { VolAddress::new(0x400_00DC) }; /// DMA 3 Control, read/write. - const DMA3CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00DE) }; + const DMA3CNT_H: VolAddress = unsafe { VolAddress::new(0x400_00DE) }; /// Assigns the source register. /// diff --git a/src/io/keypad.rs b/src/io/keypad.rs index 487c04d..477dcaa 100644 --- a/src/io/keypad.rs +++ b/src/io/keypad.rs @@ -8,7 +8,7 @@ use super::*; /// follow the "high-active" convention (hint: you probably do, it's far easier /// to work with) then call `read_key_input()` rather than reading this register /// directly. It will perform the necessary bit flip operation for you. -pub const KEYINPUT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0130) }; +pub const KEYINPUT: VolAddress = unsafe { VolAddress::new(0x400_0130) }; /// A "tribool" value helps us interpret the arrow pad. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -86,7 +86,7 @@ pub fn read_key_input() -> KeyInput { /// Use this to configure when a keypad interrupt happens. /// /// See the `KeyInterruptSetting` type for more. -pub const KEYCNT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0132) }; +pub const KEYCNT: VolAddress = unsafe { VolAddress::new(0x400_0132) }; newtype! { /// Allows configuration of when a keypad interrupt fires. diff --git a/src/io/sound.rs b/src/io/sound.rs index 46afcbc..6b6101f 100644 --- a/src/io/sound.rs +++ b/src/io/sound.rs @@ -4,7 +4,7 @@ use super::*; //TODO within these "read/write" registers only some bits are actually read/write! /// Sound Channel 1 Sweep Register (`NR10`). Read/Write. -pub const SOUND1CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0060) }; +pub const SOUND1CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0060) }; newtype! { SweepRegisterSetting, u16 @@ -20,7 +20,7 @@ impl SweepRegisterSetting { } /// Sound Channel 1 Duty/Length/Envelope (`NR11`, `NR12`). Read/Write. -pub const SOUND1CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0062) }; +pub const SOUND1CNT_H: VolAddress = unsafe { VolAddress::new(0x400_0062) }; newtype! { DutyLenEnvelopeSetting, u16 @@ -38,7 +38,7 @@ impl DutyLenEnvelopeSetting { } /// Sound Channel 1 Frequency/Control (`NR13`, `NR14`). Read/Write. -pub const SOUND1CNT_X: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0064) }; +pub const SOUND1CNT_X: VolAddress = unsafe { VolAddress::new(0x400_0064) }; newtype! { FrequencyControlSetting, u32 // TODO: u16 or u32? @@ -54,13 +54,13 @@ impl FrequencyControlSetting { } /// Sound Channel 2 Channel 2 Duty/Length/Envelope (`NR21`, `NR22`). Read/Write. -pub const SOUND2CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0068) }; +pub const SOUND2CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0068) }; /// Sound Channel 2 Frequency/Control (`NR23`, `NR24`). Read/Write. -pub const SOUND2CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_006C) }; +pub const SOUND2CNT_H: VolAddress = unsafe { VolAddress::new(0x400_006C) }; /// Sound Channel 3 Stop/Wave RAM select (`NR23`, `NR24`). Read/Write. -pub const SOUND3CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0070) }; +pub const SOUND3CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0070) }; newtype! { StopWaveRAMSelectSetting, u16 @@ -76,7 +76,7 @@ impl StopWaveRAMSelectSetting { } /// Sound Channel 3 Length/Volume (`NR23`, `NR24`). Read/Write. -pub const SOUND3CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0072) }; +pub const SOUND3CNT_H: VolAddress = unsafe { VolAddress::new(0x400_0072) }; newtype! { LengthVolumeSetting, u16 @@ -92,27 +92,27 @@ impl LengthVolumeSetting { } /// Sound Channel 3 Frequency/Control (`NR33`, `NR34`). Read/Write. -pub const SOUND3CNT_X: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0074) }; +pub const SOUND3CNT_X: VolAddress = unsafe { VolAddress::new(0x400_0074) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM0_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0090) }; +pub const WAVE_RAM0_L: VolAddress = unsafe { VolAddress::new(0x400_0090) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM0_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0092) }; +pub const WAVE_RAM0_H: VolAddress = unsafe { VolAddress::new(0x400_0092) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM1_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0094) }; +pub const WAVE_RAM1_L: VolAddress = unsafe { VolAddress::new(0x400_0094) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM1_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0096) }; +pub const WAVE_RAM1_H: VolAddress = unsafe { VolAddress::new(0x400_0096) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM2_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0098) }; +pub const WAVE_RAM2_L: VolAddress = unsafe { VolAddress::new(0x400_0098) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM2_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_009A) }; +pub const WAVE_RAM2_H: VolAddress = unsafe { VolAddress::new(0x400_009A) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM3_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_009C) }; +pub const WAVE_RAM3_L: VolAddress = unsafe { VolAddress::new(0x400_009C) }; /// Channel 3 Wave Pattern RAM (W/R) -pub const WAVE_RAM3_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_009E) }; +pub const WAVE_RAM3_H: VolAddress = unsafe { VolAddress::new(0x400_009E) }; /// Sound Channel 4 Length/Envelope (`NR41`, `NR42`). Read/Write. -pub const SOUND4CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0078) }; +pub const SOUND4CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0078) }; newtype! { LengthEnvelopeSetting, u32 // TODO: is this u32? @@ -129,7 +129,7 @@ impl LengthEnvelopeSetting { } /// Sound Channel 4 Frequency/Control (`NR43`, `NR44`). Read/Write. -pub const SOUND4CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_007C) }; +pub const SOUND4CNT_H: VolAddress = unsafe { VolAddress::new(0x400_007C) }; newtype! { NoiseFrequencySetting, u32 // TODO: is this u32? @@ -149,16 +149,16 @@ impl NoiseFrequencySetting { // TODO: unify FIFO as /// Sound A FIFO, Data 0 and Data 1 (W) -pub const FIFO_A_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00A0) }; +pub const FIFO_A_L: VolAddress = unsafe { VolAddress::new(0x400_00A0) }; /// Sound A FIFO, Data 2 and Data 3 (W) -pub const FIFO_A_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00A2) }; +pub const FIFO_A_H: VolAddress = unsafe { VolAddress::new(0x400_00A2) }; /// Sound B FIFO, Data 0 and Data 1 (W) -pub const FIFO_B_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00A4) }; +pub const FIFO_B_L: VolAddress = unsafe { VolAddress::new(0x400_00A4) }; /// Sound B FIFO, Data 2 and Data 3 (W) -pub const FIFO_B_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_00A6) }; +pub const FIFO_B_H: VolAddress = unsafe { VolAddress::new(0x400_00A6) }; /// Channel L/R Volume/Enable (`NR50`, `NR51`). Read/Write. -pub const SOUNDCNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0080) }; +pub const SOUNDCNT_L: VolAddress = unsafe { VolAddress::new(0x400_0080) }; newtype! { NonWaveVolumeEnableSetting, u16 @@ -175,7 +175,7 @@ impl NonWaveVolumeEnableSetting { } /// DMA Sound Control/Mixing. Read/Write. -pub const SOUNDCNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0082) }; +pub const SOUNDCNT_H: VolAddress = unsafe { VolAddress::new(0x400_0082) }; newtype! { WaveVolumeEnableSetting, u16 @@ -206,7 +206,7 @@ newtype_enum! { } /// Sound on/off (`NR52`). Read/Write. -pub const SOUNDCNT_X: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0084) }; +pub const SOUNDCNT_X: VolAddress = unsafe { VolAddress::new(0x400_0084) }; newtype! { SoundMasterSetting, u16 @@ -224,7 +224,7 @@ impl SoundMasterSetting { } /// Sound on/off (`NR52`). Read/Write. -pub const SOUNDBIAS: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0088) }; +pub const SOUNDBIAS: VolAddress = unsafe { VolAddress::new(0x400_0088) }; newtype! { SoundPWMSetting, u16 diff --git a/src/io/timers.rs b/src/io/timers.rs index 25cc516..1257ba4 100644 --- a/src/io/timers.rs +++ b/src/io/timers.rs @@ -28,28 +28,28 @@ use super::*; // TODO: striding blocks? /// Timer 0 Counter/Reload. Special (see module). -pub const TM0CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0100) }; +pub const TM0CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0100) }; /// Timer 1 Counter/Reload. Special (see module). -pub const TM1CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0104) }; +pub const TM1CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0104) }; /// Timer 2 Counter/Reload. Special (see module). -pub const TM2CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0108) }; +pub const TM2CNT_L: VolAddress = unsafe { VolAddress::new(0x400_0108) }; /// Timer 3 Counter/Reload. Special (see module). -pub const TM3CNT_L: VolAddress = unsafe { VolAddress::new_unchecked(0x400_010C) }; +pub const TM3CNT_L: VolAddress = unsafe { VolAddress::new(0x400_010C) }; /// Timer 0 Control. Read/Write. -pub const TM0CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0102) }; +pub const TM0CNT_H: VolAddress = unsafe { VolAddress::new(0x400_0102) }; /// Timer 1 Control. Read/Write. -pub const TM1CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0106) }; +pub const TM1CNT_H: VolAddress = unsafe { VolAddress::new(0x400_0106) }; /// Timer 2 Control. Read/Write. -pub const TM2CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_010A) }; +pub const TM2CNT_H: VolAddress = unsafe { VolAddress::new(0x400_010A) }; /// Timer 3 Control. Read/Write. -pub const TM3CNT_H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_010E) }; +pub const TM3CNT_H: VolAddress = unsafe { VolAddress::new(0x400_010E) }; newtype! { /// Allows control of a timer unit. diff --git a/src/io/window.rs b/src/io/window.rs index 0e09794..9c511af 100644 --- a/src/io/window.rs +++ b/src/io/window.rs @@ -3,10 +3,10 @@ use super::*; /// Window 0 Horizontal Dimensions (W) -pub const WIN0H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0040) }; +pub const WIN0H: VolAddress = unsafe { VolAddress::new(0x400_0040) }; /// Window 1 Horizontal Dimensions (W) -pub const WIN1H: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0042) }; +pub const WIN1H: VolAddress = unsafe { VolAddress::new(0x400_0042) }; newtype! { HorizontalWindowSetting, u16 @@ -21,10 +21,10 @@ impl HorizontalWindowSetting { } /// Window 0 Vertical Dimensions (W) -pub const WIN0V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0044) }; +pub const WIN0V: VolAddress = unsafe { VolAddress::new(0x400_0044) }; /// Window 1 Vertical Dimensions (W) -pub const WIN1V: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0046) }; +pub const WIN1V: VolAddress = unsafe { VolAddress::new(0x400_0046) }; newtype! { VerticalWindowSetting, u16 @@ -39,7 +39,7 @@ impl VerticalWindowSetting { } /// Control of Inside of Window(s) (R/W) -pub const WININ: VolAddress = unsafe { VolAddress::new_unchecked(0x400_0048) }; +pub const WININ: VolAddress = unsafe { VolAddress::new(0x400_0048) }; newtype! { InsideWindowSetting, u16 @@ -64,7 +64,7 @@ impl InsideWindowSetting { } /// Control of Outside of Windows & Inside of OBJ Window (R/W) -pub const WINOUT: VolAddress = unsafe { VolAddress::new_unchecked(0x400_004A) }; +pub const WINOUT: VolAddress = unsafe { VolAddress::new(0x400_004A) }; newtype! { OutsideWindowSetting, u16 diff --git a/src/lib.rs b/src/lib.rs index 240d76a..d263a3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ //! do, it's a giant bag of Undefined Behavior. pub(crate) use gba_proc_macro::phantom_fields; +pub(crate) use voladdress::{VolAddress, VolBlock}; /// Assists in defining a newtype wrapper over some base type. /// @@ -105,7 +106,6 @@ macro_rules! newtype_enum { } pub mod base; -pub(crate) use self::base::*; pub mod bios; diff --git a/src/mgba.rs b/src/mgba.rs index a108b1b..db70dba 100644 --- a/src/mgba.rs +++ b/src/mgba.rs @@ -24,13 +24,13 @@ pub struct MGBADebug { bytes_written: u8, } impl MGBADebug { - const ENABLE_ADDRESS: VolAddress = unsafe { VolAddress::new_unchecked(0x4fff780) }; + const ENABLE_ADDRESS: VolAddress = unsafe { VolAddress::new(0x4fff780) }; const ENABLE_ADDRESS_INPUT: u16 = 0xC0DE; const ENABLE_ADDRESS_OUTPUT: u16 = 0x1DEA; - const OUTPUT_BASE: VolAddress = unsafe { VolAddress::new_unchecked(0x4fff600) }; + const OUTPUT_BASE: VolAddress = unsafe { VolAddress::new(0x4fff600) }; - const SEND_ADDRESS: VolAddress = unsafe { VolAddress::new_unchecked(0x4fff700) }; + const SEND_ADDRESS: VolAddress = unsafe { VolAddress::new(0x4fff700) }; const SEND_FLAG: u16 = 0x100; /// Gives a new MGBADebug, if running within `mGBA` diff --git a/src/oam.rs b/src/oam.rs index b84f2e3..82b3f58 100644 --- a/src/oam.rs +++ b/src/oam.rs @@ -2,6 +2,8 @@ use super::*; +use typenum::consts::{U128, U32}; + newtype! { /// 0th part of an object's attributes. /// @@ -137,7 +139,8 @@ pub struct ObjectAttributes { /// The object attributes, but there are gaps in the array, so we must not /// expose this directly. -const OBJ_ATTR_APPROX: VolAddressBlock<[u16; 4]> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x700_0000), 128) }; +const OBJ_ATTR_APPROX: VolBlock<[u16; 4], U128> = unsafe { VolBlock::new(0x700_0000) }; +// TODO: VolSeries pub fn write_obj_attributes(slot: usize, attributes: ObjectAttributes) -> Option<()> { OBJ_ATTR_APPROX.get(slot).map(|va| unsafe { @@ -169,7 +172,8 @@ pub struct AffineParameters { /// The object attributes, but there are gaps in the array, so we must not /// expose this directly. -const AFFINE_PARAMS_APPROX: VolAddressBlock<[i16; 16]> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x700_0000), 32) }; +const AFFINE_PARAMS_APPROX: VolBlock<[i16; 16], U32> = unsafe { VolBlock::new(0x700_0000) }; +// TODO: VolSeries pub fn write_affine_parameters(slot: usize, params: AffineParameters) -> Option<()> { AFFINE_PARAMS_APPROX.get(slot).map(|va| unsafe { diff --git a/src/palram.rs b/src/palram.rs index 5158804..7d8769e 100644 --- a/src/palram.rs +++ b/src/palram.rs @@ -23,18 +23,17 @@ //! display will show if no background or object draws over top of a given pixel //! during rendering. -use super::{ - base::volatile::{VolAddress, VolAddressBlock}, - Color, -}; +use super::*; + +use typenum::consts::U256; // TODO: PalIndex newtypes? /// The `PALRAM` for background colors, 256 slot view. -pub const PALRAM_BG: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x500_0000), 256) }; +pub const PALRAM_BG: VolBlock = unsafe { VolBlock::new(0x500_0000) }; /// The `PALRAM` for object colors, 256 slot view. -pub const PALRAM_OBJ: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x500_0200), 256) }; +pub const PALRAM_OBJ: VolBlock = unsafe { VolBlock::new(0x500_0200) }; /// Obtains the address of the specified 8bpp background palette slot. pub const fn index_palram_bg_8bpp(slot: u8) -> VolAddress { diff --git a/src/vram.rs b/src/vram.rs index d4eaf9a..35138ef 100644 --- a/src/vram.rs +++ b/src/vram.rs @@ -15,6 +15,8 @@ pub(crate) use super::*; +use typenum::consts::{U256, U32, U512, U6}; + pub mod affine; pub mod bitmap; pub mod text; @@ -28,11 +30,10 @@ pub mod text; pub const VRAM_BASE_USIZE: usize = 0x600_0000; /// The character base blocks. -pub const CHAR_BASE_BLOCKS: VolAddressBlock<[u8; 0x4000]> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), 6) }; +pub const CHAR_BASE_BLOCKS: VolBlock<[u8; 0x4000], U6> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; /// The screen entry base blocks. -pub const SCREEN_BASE_BLOCKS: VolAddressBlock<[u8; 0x800]> = - unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), 32) }; +pub const SCREEN_BASE_BLOCKS: VolBlock<[u8; 0x800], U32> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; newtype! { /// An 8x8 tile with 4bpp, packed as `u32` values for proper alignment. @@ -47,11 +48,11 @@ newtype! { } /// Gives the specified charblock in 4bpp view. -pub fn get_4bpp_character_block(slot: usize) -> VolAddressBlock { - unsafe { VolAddressBlock::new_unchecked(CHAR_BASE_BLOCKS.index(slot).cast::(), 512) } +pub fn get_4bpp_character_block(slot: usize) -> VolBlock { + unsafe { VolBlock::new(CHAR_BASE_BLOCKS.index(slot).to_usize()) } } /// Gives the specified charblock in 8bpp view. -pub fn get_8bpp_character_block(slot: usize) -> VolAddressBlock { - unsafe { VolAddressBlock::new_unchecked(CHAR_BASE_BLOCKS.index(slot).cast::(), 256) } +pub fn get_8bpp_character_block(slot: usize) -> VolBlock { + unsafe { VolBlock::new(CHAR_BASE_BLOCKS.index(slot).to_usize()) } } diff --git a/src/vram/bitmap.rs b/src/vram/bitmap.rs index a716d9c..21b4ed4 100644 --- a/src/vram/bitmap.rs +++ b/src/vram/bitmap.rs @@ -2,6 +2,9 @@ use super::*; +use core::ops::{Div, Mul}; +use typenum::consts::{U128, U160, U2, U256, U4}; + /// Mode 3 is a bitmap mode with full color and full resolution. /// /// * **Width:** 240 @@ -27,12 +30,10 @@ impl Mode3 { /// /// Use `col + row * SCREEN_WIDTH` to get the address of an individual pixel, /// or use the helpers provided in this module. - pub const VRAM: VolAddressBlock = - unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_PIXEL_COUNT) }; + pub const VRAM: VolBlock>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; /// private iterator over the pixels, two at a time - const BULK_ITER: VolAddressIter = - unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_PIXEL_COUNT / 2).iter() }; + const VRAM_BULK: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; /// Reads the pixel at the given (col,row). /// @@ -56,7 +57,7 @@ impl Mode3 { pub fn clear_to(color: Color) { let color32 = color.0 as u32; let bulk_color = color32 << 16 | color32; - for va in Self::BULK_ITER { + for va in Self::VRAM_BULK.iter() { va.write(bulk_color) } } @@ -101,22 +102,22 @@ impl Mode4 { const SCREEN_U32_COUNT: usize = Self::SCREEN_PIXEL_COUNT / 4; // TODO: newtype this? - const PAGE0_BASE: VolAddress = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE) }; + const PAGE0_BLOCK8: VolBlock>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; // TODO: newtype this? - const PAGE0_BLOCK: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE, Self::SCREEN_PIXEL_COUNT) }; + const PAGE1_BLOCK8: VolBlock>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) }; // TODO: newtype this? - const PAGE1_BASE: VolAddress = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE + 0xA000) }; + const PAGE0_BLOCK16: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; // TODO: newtype this? - const PAGE1_BLOCK: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE, Self::SCREEN_PIXEL_COUNT) }; + const PAGE1_BLOCK16: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) }; /// private iterator over the page0 pixels, four at a time - const BULK_ITER0: VolAddressIter = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE.cast::(), Self::SCREEN_U32_COUNT).iter() }; + const PAGE0_BULK32: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; /// private iterator over the page1 pixels, four at a time - const BULK_ITER1: VolAddressIter = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE.cast::(), Self::SCREEN_U32_COUNT).iter() }; + const PAGE1_BULK32: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) }; /// Reads the pixel at the given (col,row). /// @@ -126,9 +127,9 @@ impl Mode4 { pub fn read_pixel(page1: bool, col: usize, row: usize) -> Option { // Note(Lokathor): byte _reads_ from VRAM are okay. if page1 { - Self::PAGE1_BLOCK.get(col + row * Self::SCREEN_WIDTH).map(VolAddress::read) + Self::PAGE1_BLOCK8.get(col + row * Self::SCREEN_WIDTH).map(VolAddress::read) } else { - Self::PAGE0_BLOCK.get(col + row * Self::SCREEN_WIDTH).map(VolAddress::read) + Self::PAGE0_BLOCK8.get(col + row * Self::SCREEN_WIDTH).map(VolAddress::read) } } @@ -145,9 +146,9 @@ impl Mode4 { let rounded_down_index = real_index & !1; let address: VolAddress = unsafe { if page1 { - Self::PAGE1_BASE.offset(rounded_down_index as isize).cast() + Self::PAGE1_BLOCK8.index(rounded_down_index).cast() } else { - Self::PAGE0_BASE.offset(rounded_down_index as isize).cast() + Self::PAGE0_BLOCK8.index(rounded_down_index).cast() } }; if real_index == rounded_down_index { @@ -173,12 +174,10 @@ impl Mode4 { pub fn write_wide_pixel(page1: bool, wide_col: usize, row: usize, wide_pal8bpp: u16) -> Option<()> { if wide_col < Self::SCREEN_WIDTH / 2 && row < Self::SCREEN_HEIGHT { let wide_index = wide_col + row * Self::SCREEN_WIDTH / 2; - let address: VolAddress = unsafe { - if page1 { - Self::PAGE1_BASE.cast::().offset(wide_index as isize) - } else { - Self::PAGE0_BASE.cast::().offset(wide_index as isize) - } + let address: VolAddress = if page1 { + Self::PAGE1_BLOCK16.index(wide_index) + } else { + Self::PAGE0_BLOCK16.index(wide_index) }; Some(address.write(wide_pal8bpp)) } else { @@ -190,7 +189,7 @@ impl Mode4 { pub fn clear_page_to(page1: bool, pal8bpp: u8) { let pal8bpp_32 = pal8bpp as u32; let bulk_color = (pal8bpp_32 << 24) | (pal8bpp_32 << 16) | (pal8bpp_32 << 8) | pal8bpp_32; - for va in if page1 { Self::BULK_ITER1 } else { Self::BULK_ITER0 } { + for va in (if page1 { Self::PAGE1_BULK32 } else { Self::PAGE0_BULK32 }).iter() { va.write(bulk_color) } } @@ -238,22 +237,16 @@ impl Mode5 { const SCREEN_U32_COUNT: usize = Self::SCREEN_PIXEL_COUNT / 2; // TODO: newtype this? - const PAGE0_BASE: VolAddress = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE) }; + const PAGE0_BLOCK: VolBlock>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; // TODO: newtype this? - const PAGE0_BLOCK: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE, Self::SCREEN_PIXEL_COUNT) }; - - // TODO: newtype this? - const PAGE1_BASE: VolAddress = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE + 0xA000) }; - - // TODO: newtype this? - const PAGE1_BLOCK: VolAddressBlock = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE, Self::SCREEN_PIXEL_COUNT) }; + const PAGE1_BLOCK: VolBlock>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) }; /// private iterator over the page0 pixels, four at a time - const BULK_ITER0: VolAddressIter = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE.cast::(), Self::SCREEN_U32_COUNT).iter() }; + const PAGE0_BULK32: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) }; /// private iterator over the page1 pixels, four at a time - const BULK_ITER1: VolAddressIter = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE.cast::(), Self::SCREEN_U32_COUNT).iter() }; + const PAGE1_BULK32: VolBlock>::Output as Div>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) }; /// Reads the pixel at the given (col,row). /// @@ -285,7 +278,7 @@ impl Mode5 { pub fn clear_page_to(page1: bool, color: Color) { let color32 = color.0 as u32; let bulk_color = color32 << 16 | color32; - for va in if page1 { Self::BULK_ITER1 } else { Self::BULK_ITER0 } { + for va in (if page1 { Self::PAGE1_BULK32 } else { Self::PAGE0_BULK32 }).iter() { va.write(bulk_color) } }