mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
Merge pull request #55 from rust-console/lokathor
Starting towards a v0.4
This commit is contained in:
commit
83f3496261
|
@ -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 <zefria@gmail.com>", "Thomas Winwood <twwinwood@gmail.com>"]
|
||||
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]
|
||||
|
|
|
@ -146,7 +146,7 @@ So, again using the `hello_magic` example, we had
|
|||
And instead we could declare
|
||||
|
||||
```rust
|
||||
const MAGIC_LOCATION: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0000) };
|
||||
const MAGIC_LOCATION: VolAddress<u16> = unsafe { VolAddress::new(0x400_0000) };
|
||||
```
|
||||
|
||||
### Using A VolAddress Value
|
||||
|
@ -300,7 +300,7 @@ Now we can have something like:
|
|||
```rust
|
||||
const OTHER_MAGIC: VolAddressBlock<u16> = unsafe {
|
||||
VolAddressBlock::new_unchecked(
|
||||
VolAddress::new_unchecked(0x600_0000),
|
||||
VolAddress::new(0x600_0000),
|
||||
240 * 160
|
||||
)
|
||||
};
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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<T> {
|
||||
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<T> Clone for VolAddress<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<T> Copy for VolAddress<T> {}
|
||||
impl<T> PartialEq for VolAddress<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.address == other.address
|
||||
}
|
||||
}
|
||||
impl<T> Eq for VolAddress<T> {}
|
||||
impl<T> PartialOrd for VolAddress<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.address.cmp(&other.address))
|
||||
}
|
||||
}
|
||||
impl<T> Ord for VolAddress<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.address.cmp(&other.address)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VolAddress<T> {
|
||||
/// 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<Z>(self) -> VolAddress<Z> {
|
||||
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::<T>())),
|
||||
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::<T>() == 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<T> {
|
||||
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<T> {
|
||||
vol_address: VolAddress<T>,
|
||||
slots: usize,
|
||||
}
|
||||
impl<T> Clone for VolAddressIter<T> {
|
||||
fn clone(&self) -> Self {
|
||||
VolAddressIter {
|
||||
vol_address: self.vol_address,
|
||||
slots: self.slots,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> PartialEq for VolAddressIter<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.vol_address == other.vol_address && self.slots == other.slots
|
||||
}
|
||||
}
|
||||
impl<T> Eq for VolAddressIter<T> {}
|
||||
impl<T> Iterator for VolAddressIter<T> {
|
||||
type Item = VolAddress<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<T> FusedIterator for VolAddressIter<T> {}
|
||||
|
||||
/// 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<T> {
|
||||
vol_address: VolAddress<T>,
|
||||
slots: usize,
|
||||
}
|
||||
impl<T> Clone for VolAddressBlock<T> {
|
||||
fn clone(&self) -> Self {
|
||||
VolAddressBlock {
|
||||
vol_address: self.vol_address,
|
||||
slots: self.slots,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> PartialEq for VolAddressBlock<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.vol_address == other.vol_address && self.slots == other.slots
|
||||
}
|
||||
}
|
||||
impl<T> Eq for VolAddressBlock<T> {}
|
||||
|
||||
impl<T> VolAddressBlock<T> {
|
||||
/// 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<T>, slots: usize) -> Self {
|
||||
VolAddressBlock { vol_address, slots }
|
||||
}
|
||||
|
||||
/// Gives an iterator over this block's slots.
|
||||
pub const fn iter(self) -> VolAddressIter<T> {
|
||||
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<T> {
|
||||
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<T> {
|
||||
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<VolAddress<T>> {
|
||||
if slot < self.slots {
|
||||
unsafe { Some(self.vol_address.offset(slot as isize)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,13 +3,13 @@
|
|||
use super::*;
|
||||
|
||||
/// BG0 Control. Read/Write. Display Mode 0/1 only.
|
||||
pub const BG0CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0008) };
|
||||
pub const BG0CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new(0x400_0008) };
|
||||
/// BG1 Control. Read/Write. Display Mode 0/1 only.
|
||||
pub const BG1CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000A) };
|
||||
pub const BG1CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new(0x400_000A) };
|
||||
/// BG2 Control. Read/Write. Display Mode 0/1/2 only.
|
||||
pub const BG2CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000C) };
|
||||
pub const BG2CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new(0x400_000C) };
|
||||
/// BG3 Control. Read/Write. Display Mode 0/2 only.
|
||||
pub const BG3CNT: VolAddress<BackgroundControlSetting> = unsafe { VolAddress::new_unchecked(0x400_000E) };
|
||||
pub const BG3CNT: VolAddress<BackgroundControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_0010) };
|
||||
pub const BG0HOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_0010) };
|
||||
/// BG0 Y-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG0VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0012) };
|
||||
pub const BG0VOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_0012) };
|
||||
|
||||
/// BG1 X-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG1HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0014) };
|
||||
pub const BG1HOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_0014) };
|
||||
/// BG1 Y-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG1VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0016) };
|
||||
pub const BG1VOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_0016) };
|
||||
|
||||
/// BG2 X-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG2HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0018) };
|
||||
pub const BG2HOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_0018) };
|
||||
/// BG2 Y-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG2VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_001A) };
|
||||
pub const BG2VOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_001A) };
|
||||
|
||||
/// BG3 X-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG3HOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_001C) };
|
||||
pub const BG3HOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_001C) };
|
||||
/// BG3 Y-Offset. Write only. Text mode only. 9 bits.
|
||||
pub const BG3VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_001E) };
|
||||
pub const BG3VOFS: VolAddress<u16> = unsafe { VolAddress::new(0x400_001E) };
|
||||
|
||||
// TODO: affine backgrounds
|
||||
// BG2X_L
|
||||
|
@ -100,14 +100,14 @@ pub const BG3VOFS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00
|
|||
// BG3PD
|
||||
|
||||
// TODO: windowing
|
||||
// pub const WIN0H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0040) };
|
||||
// pub const WIN1H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0042) };
|
||||
// pub const WIN0V: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0044) };
|
||||
// pub const WIN1V: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0046) };
|
||||
// pub const WININ: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0048) };
|
||||
// pub const WINOUT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_004A) };
|
||||
// pub const WIN0H: VolAddress<u16> = unsafe { VolAddress::new(0x400_0040) };
|
||||
// pub const WIN1H: VolAddress<u16> = unsafe { VolAddress::new(0x400_0042) };
|
||||
// pub const WIN0V: VolAddress<u16> = unsafe { VolAddress::new(0x400_0044) };
|
||||
// pub const WIN1V: VolAddress<u16> = unsafe { VolAddress::new(0x400_0046) };
|
||||
// pub const WININ: VolAddress<u16> = unsafe { VolAddress::new(0x400_0048) };
|
||||
// pub const WINOUT: VolAddress<u16> = unsafe { VolAddress::new(0x400_004A) };
|
||||
|
||||
// TODO: blending
|
||||
// pub const BLDCNT: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0050) };
|
||||
// pub const BLDALPHA: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0052) };
|
||||
// pub const BLDY: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0054) };
|
||||
// pub const BLDCNT: VolAddress<u16> = unsafe { VolAddress::new(0x400_0050) };
|
||||
// pub const BLDALPHA: VolAddress<u16> = unsafe { VolAddress::new(0x400_0052) };
|
||||
// pub const BLDY: VolAddress<u16> = unsafe { VolAddress::new(0x400_0054) };
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use super::*;
|
||||
|
||||
/// Color Special Effects Selection (R/W)
|
||||
pub const BLDCNT: VolAddress<ColorEffectSetting> = unsafe { VolAddress::new_unchecked(0x400_0050) };
|
||||
pub const BLDCNT: VolAddress<ColorEffectSetting> = 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<AlphaBlendingSetting> = unsafe { VolAddress::new_unchecked(0x400_0052) };
|
||||
pub const BLDALPHA: VolAddress<AlphaBlendingSetting> = 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<BrightnessSetting> = unsafe { VolAddress::new_unchecked(0x400_0054) };
|
||||
pub const BLDY: VolAddress<BrightnessSetting> = unsafe { VolAddress::new(0x400_0054) };
|
||||
|
||||
newtype! {
|
||||
BrightnessSetting, u32
|
||||
|
|
|
@ -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<DisplayControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0000) };
|
||||
pub const DISPCNT: VolAddress<DisplayControlSetting> = 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<DisplayStatusSetting> = unsafe { VolAddress::new_unchecked(0x400_0004) };
|
||||
pub const DISPSTAT: VolAddress<DisplayStatusSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_0006) };
|
||||
pub const VCOUNT: VolAddress<u16> = 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<MosaicSetting> = unsafe { VolAddress::new_unchecked(0x400_004C) };
|
||||
pub const MOSAIC: VolAddress<MosaicSetting> = unsafe { VolAddress::new(0x400_004C) };
|
||||
|
||||
newtype! {
|
||||
/// Allows control of the Mosaic effect.
|
||||
|
|
|
@ -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<u16> = unsafe { VolAddress::new_unchecked(0x400_00B8) };
|
||||
const DMA0CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00B8) };
|
||||
/// DMA 0 Control, read/write.
|
||||
const DMA0CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00BA) };
|
||||
const DMA0CNT_H: VolAddress<DMAControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00C4) };
|
||||
const DMA1CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00C4) };
|
||||
/// DMA 1 Control, read/write.
|
||||
const DMA1CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00C6) };
|
||||
const DMA1CNT_H: VolAddress<DMAControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00D0) };
|
||||
const DMA2CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00D0) };
|
||||
/// DMA 2 Control, read/write.
|
||||
const DMA2CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00D2) };
|
||||
const DMA2CNT_H: VolAddress<DMAControlSetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00DC) };
|
||||
const DMA3CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00DC) };
|
||||
/// DMA 3 Control, read/write.
|
||||
const DMA3CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new_unchecked(0x400_00DE) };
|
||||
const DMA3CNT_H: VolAddress<DMAControlSetting> = unsafe { VolAddress::new(0x400_00DE) };
|
||||
|
||||
/// Assigns the source register.
|
||||
///
|
||||
|
|
|
@ -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<u16> = unsafe { VolAddress::new_unchecked(0x400_0130) };
|
||||
pub const KEYINPUT: VolAddress<u16> = 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<KeyInterruptSetting> = unsafe { VolAddress::new_unchecked(0x400_0132) };
|
||||
pub const KEYCNT: VolAddress<KeyInterruptSetting> = unsafe { VolAddress::new(0x400_0132) };
|
||||
|
||||
newtype! {
|
||||
/// Allows configuration of when a keypad interrupt fires.
|
||||
|
|
|
@ -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<SweepRegisterSetting> = unsafe { VolAddress::new_unchecked(0x400_0060) };
|
||||
pub const SOUND1CNT_L: VolAddress<SweepRegisterSetting> = 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<DutyLenEnvelopeSetting> = unsafe { VolAddress::new_unchecked(0x400_0062) };
|
||||
pub const SOUND1CNT_H: VolAddress<DutyLenEnvelopeSetting> = 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<FrequencyControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0064) };
|
||||
pub const SOUND1CNT_X: VolAddress<FrequencyControlSetting> = 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<DutyLenEnvelopeSetting> = unsafe { VolAddress::new_unchecked(0x400_0068) };
|
||||
pub const SOUND2CNT_L: VolAddress<DutyLenEnvelopeSetting> = unsafe { VolAddress::new(0x400_0068) };
|
||||
|
||||
/// Sound Channel 2 Frequency/Control (`NR23`, `NR24`). Read/Write.
|
||||
pub const SOUND2CNT_H: VolAddress<FrequencyControlSetting> = unsafe { VolAddress::new_unchecked(0x400_006C) };
|
||||
pub const SOUND2CNT_H: VolAddress<FrequencyControlSetting> = unsafe { VolAddress::new(0x400_006C) };
|
||||
|
||||
/// Sound Channel 3 Stop/Wave RAM select (`NR23`, `NR24`). Read/Write.
|
||||
pub const SOUND3CNT_L: VolAddress<StopWaveRAMSelectSetting> = unsafe { VolAddress::new_unchecked(0x400_0070) };
|
||||
pub const SOUND3CNT_L: VolAddress<StopWaveRAMSelectSetting> = 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<LengthVolumeSetting> = unsafe { VolAddress::new_unchecked(0x400_0072) };
|
||||
pub const SOUND3CNT_H: VolAddress<LengthVolumeSetting> = 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<FrequencyControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0074) };
|
||||
pub const SOUND3CNT_X: VolAddress<FrequencyControlSetting> = unsafe { VolAddress::new(0x400_0074) };
|
||||
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM0_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0090) };
|
||||
pub const WAVE_RAM0_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0090) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM0_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0092) };
|
||||
pub const WAVE_RAM0_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_0092) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM1_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0094) };
|
||||
pub const WAVE_RAM1_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0094) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM1_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0096) };
|
||||
pub const WAVE_RAM1_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_0096) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM2_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0098) };
|
||||
pub const WAVE_RAM2_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0098) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM2_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_009A) };
|
||||
pub const WAVE_RAM2_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_009A) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM3_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_009C) };
|
||||
pub const WAVE_RAM3_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_009C) };
|
||||
/// Channel 3 Wave Pattern RAM (W/R)
|
||||
pub const WAVE_RAM3_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_009E) };
|
||||
pub const WAVE_RAM3_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_009E) };
|
||||
|
||||
/// Sound Channel 4 Length/Envelope (`NR41`, `NR42`). Read/Write.
|
||||
pub const SOUND4CNT_L: VolAddress<LengthEnvelopeSetting> = unsafe { VolAddress::new_unchecked(0x400_0078) };
|
||||
pub const SOUND4CNT_L: VolAddress<LengthEnvelopeSetting> = 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<NoiseFrequencySetting> = unsafe { VolAddress::new_unchecked(0x400_007C) };
|
||||
pub const SOUND4CNT_H: VolAddress<NoiseFrequencySetting> = 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<u16> = unsafe { VolAddress::new_unchecked(0x400_00A0) };
|
||||
pub const FIFO_A_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00A0) };
|
||||
/// Sound A FIFO, Data 2 and Data 3 (W)
|
||||
pub const FIFO_A_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00A2) };
|
||||
pub const FIFO_A_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_00A2) };
|
||||
/// Sound B FIFO, Data 0 and Data 1 (W)
|
||||
pub const FIFO_B_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00A4) };
|
||||
pub const FIFO_B_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_00A4) };
|
||||
/// Sound B FIFO, Data 2 and Data 3 (W)
|
||||
pub const FIFO_B_H: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_00A6) };
|
||||
pub const FIFO_B_H: VolAddress<u16> = unsafe { VolAddress::new(0x400_00A6) };
|
||||
|
||||
/// Channel L/R Volume/Enable (`NR50`, `NR51`). Read/Write.
|
||||
pub const SOUNDCNT_L: VolAddress<NonWaveVolumeEnableSetting> = unsafe { VolAddress::new_unchecked(0x400_0080) };
|
||||
pub const SOUNDCNT_L: VolAddress<NonWaveVolumeEnableSetting> = 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<WaveVolumeEnableSetting> = unsafe { VolAddress::new_unchecked(0x400_0082) };
|
||||
pub const SOUNDCNT_H: VolAddress<WaveVolumeEnableSetting> = 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<SoundMasterSetting> = unsafe { VolAddress::new_unchecked(0x400_0084) };
|
||||
pub const SOUNDCNT_X: VolAddress<SoundMasterSetting> = unsafe { VolAddress::new(0x400_0084) };
|
||||
|
||||
newtype! {
|
||||
SoundMasterSetting, u16
|
||||
|
@ -224,7 +224,7 @@ impl SoundMasterSetting {
|
|||
}
|
||||
|
||||
/// Sound on/off (`NR52`). Read/Write.
|
||||
pub const SOUNDBIAS: VolAddress<SoundPWMSetting> = unsafe { VolAddress::new_unchecked(0x400_0088) };
|
||||
pub const SOUNDBIAS: VolAddress<SoundPWMSetting> = unsafe { VolAddress::new(0x400_0088) };
|
||||
|
||||
newtype! {
|
||||
SoundPWMSetting, u16
|
||||
|
|
|
@ -28,28 +28,28 @@ use super::*;
|
|||
// TODO: striding blocks?
|
||||
|
||||
/// Timer 0 Counter/Reload. Special (see module).
|
||||
pub const TM0CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0100) };
|
||||
pub const TM0CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0100) };
|
||||
|
||||
/// Timer 1 Counter/Reload. Special (see module).
|
||||
pub const TM1CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0104) };
|
||||
pub const TM1CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0104) };
|
||||
|
||||
/// Timer 2 Counter/Reload. Special (see module).
|
||||
pub const TM2CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_0108) };
|
||||
pub const TM2CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_0108) };
|
||||
|
||||
/// Timer 3 Counter/Reload. Special (see module).
|
||||
pub const TM3CNT_L: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x400_010C) };
|
||||
pub const TM3CNT_L: VolAddress<u16> = unsafe { VolAddress::new(0x400_010C) };
|
||||
|
||||
/// Timer 0 Control. Read/Write.
|
||||
pub const TM0CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0102) };
|
||||
pub const TM0CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new(0x400_0102) };
|
||||
|
||||
/// Timer 1 Control. Read/Write.
|
||||
pub const TM1CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_0106) };
|
||||
pub const TM1CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new(0x400_0106) };
|
||||
|
||||
/// Timer 2 Control. Read/Write.
|
||||
pub const TM2CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_010A) };
|
||||
pub const TM2CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new(0x400_010A) };
|
||||
|
||||
/// Timer 3 Control. Read/Write.
|
||||
pub const TM3CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new_unchecked(0x400_010E) };
|
||||
pub const TM3CNT_H: VolAddress<TimerControlSetting> = unsafe { VolAddress::new(0x400_010E) };
|
||||
|
||||
newtype! {
|
||||
/// Allows control of a timer unit.
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
use super::*;
|
||||
|
||||
/// Window 0 Horizontal Dimensions (W)
|
||||
pub const WIN0H: VolAddress<HorizontalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0040) };
|
||||
pub const WIN0H: VolAddress<HorizontalWindowSetting> = unsafe { VolAddress::new(0x400_0040) };
|
||||
|
||||
/// Window 1 Horizontal Dimensions (W)
|
||||
pub const WIN1H: VolAddress<HorizontalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0042) };
|
||||
pub const WIN1H: VolAddress<HorizontalWindowSetting> = unsafe { VolAddress::new(0x400_0042) };
|
||||
|
||||
newtype! {
|
||||
HorizontalWindowSetting, u16
|
||||
|
@ -21,10 +21,10 @@ impl HorizontalWindowSetting {
|
|||
}
|
||||
|
||||
/// Window 0 Vertical Dimensions (W)
|
||||
pub const WIN0V: VolAddress<VerticalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0044) };
|
||||
pub const WIN0V: VolAddress<VerticalWindowSetting> = unsafe { VolAddress::new(0x400_0044) };
|
||||
|
||||
/// Window 1 Vertical Dimensions (W)
|
||||
pub const WIN1V: VolAddress<VerticalWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0046) };
|
||||
pub const WIN1V: VolAddress<VerticalWindowSetting> = 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<InsideWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_0048) };
|
||||
pub const WININ: VolAddress<InsideWindowSetting> = 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<OutsideWindowSetting> = unsafe { VolAddress::new_unchecked(0x400_004A) };
|
||||
pub const WINOUT: VolAddress<OutsideWindowSetting> = unsafe { VolAddress::new(0x400_004A) };
|
||||
|
||||
newtype! {
|
||||
OutsideWindowSetting, u16
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -24,13 +24,13 @@ pub struct MGBADebug {
|
|||
bytes_written: u8,
|
||||
}
|
||||
impl MGBADebug {
|
||||
const ENABLE_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x4fff780) };
|
||||
const ENABLE_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new(0x4fff780) };
|
||||
const ENABLE_ADDRESS_INPUT: u16 = 0xC0DE;
|
||||
const ENABLE_ADDRESS_OUTPUT: u16 = 0x1DEA;
|
||||
|
||||
const OUTPUT_BASE: VolAddress<u8> = unsafe { VolAddress::new_unchecked(0x4fff600) };
|
||||
const OUTPUT_BASE: VolAddress<u8> = unsafe { VolAddress::new(0x4fff600) };
|
||||
|
||||
const SEND_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new_unchecked(0x4fff700) };
|
||||
const SEND_ADDRESS: VolAddress<u16> = unsafe { VolAddress::new(0x4fff700) };
|
||||
const SEND_FLAG: u16 = 0x100;
|
||||
|
||||
/// Gives a new MGBADebug, if running within `mGBA`
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<Color> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x500_0000), 256) };
|
||||
pub const PALRAM_BG: VolBlock<Color, U256> = unsafe { VolBlock::new(0x500_0000) };
|
||||
|
||||
/// The `PALRAM` for object colors, 256 slot view.
|
||||
pub const PALRAM_OBJ: VolAddressBlock<Color> = unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(0x500_0200), 256) };
|
||||
pub const PALRAM_OBJ: VolBlock<Color, U256> = 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<Color> {
|
||||
|
|
15
src/vram.rs
15
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<Tile4bpp> {
|
||||
unsafe { VolAddressBlock::new_unchecked(CHAR_BASE_BLOCKS.index(slot).cast::<Tile4bpp>(), 512) }
|
||||
pub fn get_4bpp_character_block(slot: usize) -> VolBlock<Tile4bpp, U512> {
|
||||
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<Tile8bpp> {
|
||||
unsafe { VolAddressBlock::new_unchecked(CHAR_BASE_BLOCKS.index(slot).cast::<Tile8bpp>(), 256) }
|
||||
pub fn get_8bpp_character_block(slot: usize) -> VolBlock<Tile8bpp, U256> {
|
||||
unsafe { VolBlock::new(CHAR_BASE_BLOCKS.index(slot).to_usize()) }
|
||||
}
|
||||
|
|
|
@ -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<Color> =
|
||||
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_PIXEL_COUNT) };
|
||||
pub const VRAM: VolBlock<Color, <U256 as Mul<U160>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
/// private iterator over the pixels, two at a time
|
||||
const BULK_ITER: VolAddressIter<u32> =
|
||||
unsafe { VolAddressBlock::new_unchecked(VolAddress::new_unchecked(VRAM_BASE_USIZE), Self::SCREEN_PIXEL_COUNT / 2).iter() };
|
||||
const VRAM_BULK: VolBlock<u32, <<U256 as Mul<U160>>::Output as Div<U2>>::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<u8> = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE) };
|
||||
const PAGE0_BLOCK8: VolBlock<u8, <U256 as Mul<U160>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE0_BLOCK: VolAddressBlock<u8> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE, Self::SCREEN_PIXEL_COUNT) };
|
||||
const PAGE1_BLOCK8: VolBlock<u8, <U256 as Mul<U160>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE1_BASE: VolAddress<u8> = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE + 0xA000) };
|
||||
const PAGE0_BLOCK16: VolBlock<u16, <<U256 as Mul<U160>>::Output as Div<U2>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE1_BLOCK: VolAddressBlock<u8> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE, Self::SCREEN_PIXEL_COUNT) };
|
||||
const PAGE1_BLOCK16: VolBlock<u16, <<U256 as Mul<U160>>::Output as Div<U2>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) };
|
||||
|
||||
/// private iterator over the page0 pixels, four at a time
|
||||
const BULK_ITER0: VolAddressIter<u32> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE.cast::<u32>(), Self::SCREEN_U32_COUNT).iter() };
|
||||
const PAGE0_BULK32: VolBlock<u32, <<U256 as Mul<U160>>::Output as Div<U4>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
/// private iterator over the page1 pixels, four at a time
|
||||
const BULK_ITER1: VolAddressIter<u32> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE.cast::<u32>(), Self::SCREEN_U32_COUNT).iter() };
|
||||
const PAGE1_BULK32: VolBlock<u32, <<U256 as Mul<U160>>::Output as Div<U4>>::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<u8> {
|
||||
// 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<u16> = 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<u16> = unsafe {
|
||||
if page1 {
|
||||
Self::PAGE1_BASE.cast::<u16>().offset(wide_index as isize)
|
||||
let address: VolAddress<u16> = if page1 {
|
||||
Self::PAGE1_BLOCK16.index(wide_index)
|
||||
} else {
|
||||
Self::PAGE0_BASE.cast::<u16>().offset(wide_index as isize)
|
||||
}
|
||||
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<Color> = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE) };
|
||||
const PAGE0_BLOCK: VolBlock<Color, <U160 as Mul<U128>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE0_BLOCK: VolAddressBlock<Color> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE, Self::SCREEN_PIXEL_COUNT) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE1_BASE: VolAddress<Color> = unsafe { VolAddress::new_unchecked(VRAM_BASE_USIZE + 0xA000) };
|
||||
|
||||
// TODO: newtype this?
|
||||
const PAGE1_BLOCK: VolAddressBlock<Color> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE, Self::SCREEN_PIXEL_COUNT) };
|
||||
const PAGE1_BLOCK: VolBlock<Color, <U160 as Mul<U128>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE + 0xA000) };
|
||||
|
||||
/// private iterator over the page0 pixels, four at a time
|
||||
const BULK_ITER0: VolAddressIter<u32> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE0_BASE.cast::<u32>(), Self::SCREEN_U32_COUNT).iter() };
|
||||
const PAGE0_BULK32: VolBlock<u32, <<U160 as Mul<U128>>::Output as Div<U2>>::Output> = unsafe { VolBlock::new(VRAM_BASE_USIZE) };
|
||||
|
||||
/// private iterator over the page1 pixels, four at a time
|
||||
const BULK_ITER1: VolAddressIter<u32> = unsafe { VolAddressBlock::new_unchecked(Self::PAGE1_BASE.cast::<u32>(), Self::SCREEN_U32_COUNT).iter() };
|
||||
const PAGE1_BULK32: VolBlock<u32, <<U160 as Mul<U128>>::Output as Div<U2>>::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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue