#![no_std]
#![deny(missing_docs)]
#![allow(unused_imports)]
#![cfg_attr(not(feature = "unsafe_addresses"), allow(dead_code))]
use core::mem::size_of;
macro_rules! pub_const_fn_new_zero {
() => {
pub const fn new() -> Self {
Self(0)
}
};
}
#[rustfmt::skip]
macro_rules! bit {
(0) => { 0b1 };
(1) => { 0b10 };
(2) => { 0b100 };
(3) => { 0b1000 };
(4) => { 0b1_0000 };
(5) => { 0b10_0000 };
(6) => { 0b100_0000 };
(7) => { 0b1000_0000 };
(8) => { 0b1_0000_0000 };
(9) => { 0b10_0000_0000 };
(10) => { 0b100_0000_0000 };
(11) => { 0b1000_0000_0000 };
(12) => { 0b1_0000_0000_0000 };
(13) => { 0b10_0000_0000_0000 };
(14) => { 0b100_0000_0000_0000 };
(15) => { 0b1000_0000_0000_0000 };
}
macro_rules! bool_bit_u16 {
($bit:tt, $get:ident, $with:ident, $set:ident) => {
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $get(self) -> bool {
any_in_mask(self.0 as u32, bit!($bit))
}
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $with(self, b: bool) -> Self {
Self(force_mask_bits(self.0 as u32, bit!($bit), b) as u16)
}
#[inline(always)]
#[allow(missing_docs)]
pub fn $set(&mut self, b: bool) {
*self = self.$with(b);
}
};
}
macro_rules! const_enum_bits_u16 {
($the_type:tt, $mask:literal, $get:ident, $with:ident, $set:ident) => {
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $get(self) -> $the_type {
$the_type(self.0 & $mask)
}
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $with(self, t: $the_type) -> Self {
Self(merge_mask_bits(self.0 as u32, t.0 as u32, $mask) as u16)
}
#[inline(always)]
#[allow(missing_docs)]
pub fn $set(&mut self, t: $the_type) {
*self = self.$with(t);
}
};
}
macro_rules! unsigned_bits_u16 {
($low_bit:literal ..= $high_bit:literal, $get:ident, $with:ident, $set:ident) => {
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $get(self) -> u16 {
const MASK: u32 = (0b1 << ($high_bit - $low_bit + 1)) - 1;
const SHIFT: u32 = u16::trailing_zeros(MASK as u16);
const BASE_MASK: u32 = (MASK >> SHIFT) as u32;
((self.0 as u32 >> SHIFT) & BASE_MASK) as u16
}
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $with(self, u: u16) -> Self {
const BASE_MASK: u32 = (0b1 << ($high_bit - $low_bit + 1)) - 1;
const SHIFT: u32 = $low_bit;
const MASK: u32 = BASE_MASK << SHIFT;
let new_bits = (u as u32 & BASE_MASK) << SHIFT;
Self(merge_mask_bits(self.0 as u32, new_bits, MASK) as u16)
}
#[inline(always)]
#[allow(missing_docs)]
pub fn $set(&mut self, u: u16) {
*self = self.$with(u);
}
};
}
#[rustfmt::skip]
macro_rules! bit32 {
(0) => { 0b1 };
(1) => { 0b10 };
(2) => { 0b100 };
(3) => { 0b1000 };
(4) => { 0b1_0000 };
(5) => { 0b10_0000 };
(6) => { 0b100_0000 };
(7) => { 0b1000_0000 };
(8) => { 0b1_0000_0000 };
(9) => { 0b10_0000_0000 };
(10) => { 0b100_0000_0000 };
(11) => { 0b1000_0000_0000 };
(12) => { 0b1_0000_0000_0000 };
(13) => { 0b10_0000_0000_0000 };
(14) => { 0b100_0000_0000_0000 };
(15) => { 0b1000_0000_0000_0000 };
(16) => { 0b1_0000_0000_0000_0000 };
(17) => { 0b10_0000_0000_0000_0000 };
(18) => { 0b100_0000_0000_0000_0000 };
(19) => { 0b1000_0000_0000_0000_0000 };
(20) => { 0b1_0000_0000_0000_0000_0000 };
(21) => { 0b10_0000_0000_0000_0000_0000 };
(22) => { 0b100_0000_0000_0000_0000_0000 };
(23) => { 0b1000_0000_0000_0000_0000_0000 };
(24) => { 0b1_0000_0000_0000_0000_0000_0000 };
(25) => { 0b10_0000_0000_0000_0000_0000_0000 };
(26) => { 0b100_0000_0000_0000_0000_0000_0000 };
(27) => { 0b1000_0000_0000_0000_0000_0000_0000 };
(28) => { 0b1_0000_0000_0000_0000_0000_0000_0000 };
(29) => { 0b10_0000_0000_0000_0000_0000_0000_0000 };
(30) => { 0b100_0000_0000_0000_0000_0000_0000_0000 };
(31) => { 0b1000_0000_0000_0000_0000_0000_0000_0000 };
}
macro_rules! bool_bit_u32 {
($bit:tt, $get:ident, $with:ident, $set:ident) => {
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $get(self) -> bool {
any_in_mask(self.0, bit32!($bit))
}
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $with(self, b: bool) -> Self {
Self(force_mask_bits(self.0, bit32!($bit), b))
}
#[inline(always)]
#[allow(missing_docs)]
pub fn $set(&mut self, b: bool) {
*self = self.$with(b);
}
};
}
macro_rules! unsigned_bits_u32 {
($low_bit:literal ..= $high_bit:literal, $get:ident, $with:ident, $set:ident) => {
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $get(self) -> u32 {
const MASK: u32 = (0b1 << ($high_bit - $low_bit + 1)) - 1;
const SHIFT: u32 = u32::trailing_zeros(MASK);
const BASE_MASK: u32 = (MASK >> SHIFT);
((self.0 >> SHIFT) & BASE_MASK)
}
#[must_use]
#[inline(always)]
#[allow(missing_docs)]
pub const fn $with(self, u: u32) -> Self {
const BASE_MASK: u32 = (0b1 << ($high_bit - $low_bit + 1)) - 1;
const SHIFT: u32 = $low_bit;
const MASK: u32 = BASE_MASK << SHIFT;
let new_bits = (u & BASE_MASK) << SHIFT;
Self(merge_mask_bits(self.0, new_bits, MASK))
}
#[inline(always)]
#[allow(missing_docs)]
pub fn $set(&mut self, u: u32) {
*self = self.$with(u);
}
};
}
macro_rules! submodule {
($v:vis $name:ident) => {
mod $name;
$v use $name::*;
};
($v:vis $name:ident { $($content:tt)* }) => {
mod $name { $($content)* }
$v use $name::*;
};
}
submodule!(pub video {
use super::*;
submodule!(pub bg_affine);
submodule!(pub bg_cnt);
submodule!(pub bg_ofs);
submodule!(pub blending);
submodule!(pub dispcnt);
submodule!(pub dispstat);
submodule!(pub mosaic);
submodule!(pub vcount);
submodule!(pub video_mode);
submodule!(pub window);
});
submodule!(pub volatile {
use super::*;
use core::{
marker::PhantomData,
num::NonZeroUsize,
ptr::{read_volatile, write_volatile},
};
macro_rules! impl_vol_eq {
($name:ty) => {
impl<T> PartialEq for $name {
fn eq(&self, other: &Self) -> bool {
self.addr.eq(&other.addr)
}
}
impl<T> Eq for $name {}
}
}
submodule!(pub danger_write_vol_addr);
submodule!(pub read_only_vol_addr);
submodule!(pub simple_vol_addr);
submodule!(pub write_only_vol_addr);
});
submodule!(pub vram {
use super::*;
pub const VRAM: usize = 0x0600_0000;
#[cfg(feature = "unsafe_addresses")]
submodule!(pub mode3);
#[cfg(feature = "unsafe_addresses")]
submodule!(pub mode4);
#[cfg(feature = "unsafe_addresses")]
submodule!(pub mode5);
});
#[must_use]
#[inline(always)]
pub(crate) const fn any_in_mask(val: u32, mask: u32) -> bool {
val & mask > 0
}
#[must_use]
#[inline(always)]
pub(crate) const fn force_mask_bits(val: u32, mask: u32, b: bool) -> u32 {
let f = b as u32;
let neg_f = f.wrapping_neg();
val ^ ((neg_f ^ val) & mask)
}
#[must_use]
#[inline(always)]
pub(crate) const fn merge_mask_bits(
not_in_mask: u32,
in_mask: u32,
mask: u32,
) -> u32 {
let a = not_in_mask;
let b = in_mask;
a ^ ((a ^ b) & mask)
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Color(pub u16);
#[allow(missing_docs)]
impl Color {
pub const fn from_rgb(r: u16, g: u16, b: u16) -> Self {
Self((b << 10) | (g << 5) | r)
}
unsigned_bits_u16!(0..=4, red, with_red, set_red);
unsigned_bits_u16!(5..=9, green, with_green, set_green);
unsigned_bits_u16!(10..=14, blue, with_blue, set_blue);
}
#[test]
fn color_value_check() {
assert_eq!(Color::from_rgb(31, 0, 0).0, 0b11111);
assert_eq!(Color::from_rgb(0, 31, 0).0, 0b1111100000);
assert_eq!(Color::from_rgb(0, 0, 31).0, 0b111110000000000);
assert_eq!(Color::from_rgb(31, 0, 0), Color(0).with_red(31));
assert_eq!(Color::from_rgb(0, 31, 0), Color(0).with_green(31));
assert_eq!(Color::from_rgb(0, 0, 31), Color(0).with_blue(31));
}
#[derive(Debug, Clone, Copy, Default)]
pub struct LowActiveKeys(pub u16);
#[cfg(feature = "unsafe_addresses")]
pub const KEYINPUT: ReadOnlyVolAddr<LowActiveKeys> =
unsafe { ReadOnlyVolAddr::new(0x0400_0130) };
#[derive(Debug, Clone, Copy, Default)]
pub struct HighActiveKeys(pub u16);
#[allow(missing_docs)]
impl HighActiveKeys {
pub_const_fn_new_zero!();
bool_bit_u16!(0, a, with_a, set_a);
bool_bit_u16!(1, b, with_b, set_b);
bool_bit_u16!(2, select, with_select, set_select);
bool_bit_u16!(3, start, with_start, set_start);
bool_bit_u16!(4, right, with_right, set_right);
bool_bit_u16!(5, left, with_left, set_left);
bool_bit_u16!(6, up, with_up, set_up);
bool_bit_u16!(7, down, with_down, set_down);
bool_bit_u16!(8, r, with_r, set_r);
bool_bit_u16!(9, l, with_l, set_l);
}
impl From<LowActiveKeys> for HighActiveKeys {
fn from(low: LowActiveKeys) -> Self {
HighActiveKeys(low.0 ^ 0b11_1111_1111)
}
}
#[derive(Debug, Clone, Copy)]
#[cfg(feature = "unsafe_addresses")]
pub struct PALRAM;
#[cfg(feature = "unsafe_addresses")]
impl PALRAM {
const BG_ADDR: usize = 0x0500_0000;
const OBJ_ADDR: usize = 0x0500_0200;
#[must_use]
#[inline(always)]
pub const fn bg_8bpp(self, index: u8) -> SimpleVolAddr<Color> {
unsafe {
SimpleVolAddr::new(PALRAM::BG_ADDR + size_of::<Color>() * index as usize)
}
}
#[must_use]
#[inline(always)]
pub const fn obj_8bpp(self, index: u8) -> SimpleVolAddr<Color> {
unsafe {
SimpleVolAddr::new(PALRAM::OBJ_ADDR + size_of::<Color>() * index as usize)
}
}
#[must_use]
#[inline(always)]
pub const fn backdrop(self) -> SimpleVolAddr<Color> {
unsafe { SimpleVolAddr::new(PALRAM::BG_ADDR) }
}
#[must_use]
#[inline(always)]
pub fn bg_4bpp(self, pal_bank: usize, entry: usize) -> SimpleVolAddr<Color> {
assert!(pal_bank < 16);
assert!(entry < 16);
let index = pal_bank << 4 | entry;
unsafe { SimpleVolAddr::new(PALRAM::BG_ADDR + size_of::<Color>() * index) }
}
#[must_use]
#[inline(always)]
pub fn obj_4bpp(self, pal_bank: usize, entry: usize) -> SimpleVolAddr<Color> {
assert!(pal_bank < 16);
assert!(entry < 16);
let index = pal_bank << 4 | entry;
unsafe { SimpleVolAddr::new(PALRAM::OBJ_ADDR + size_of::<Color>() * index) }
}
#[must_use]
#[inline(always)]
pub const fn bg_4bpp_wrapping(
self,
pal_bank: usize,
entry: usize,
) -> SimpleVolAddr<Color> {
let pal_bank = pal_bank & 0b1111;
let entry = entry & 0b1111;
let index = pal_bank << 4 | entry;
unsafe { SimpleVolAddr::new(PALRAM::BG_ADDR + size_of::<Color>() * index) }
}
#[must_use]
#[inline(always)]
pub const fn obj_4bpp_wrapping(
self,
pal_bank: usize,
entry: usize,
) -> SimpleVolAddr<Color> {
let pal_bank = pal_bank & 0b1111;
let entry = entry & 0b1111;
let index = pal_bank << 4 | entry;
unsafe { SimpleVolAddr::new(PALRAM::OBJ_ADDR + size_of::<Color>() * index) }
}
}