mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
fixed point and stuff
This commit is contained in:
parent
046e80851f
commit
58d739dd9e
|
@ -12,6 +12,7 @@ license = "Apache-2.0"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
typenum = "1.10"
|
||||
gba-proc-macro = "0.2.1"
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -89,10 +89,6 @@ the standard library types to be used "for free" once it was set up, or just a
|
|||
custom allocator that's GBA specific if Rust's global allocator style isn't a
|
||||
good fit for the GBA (I honestly haven't looked into it).
|
||||
|
||||
## LLVM Intrinsics
|
||||
|
||||
TODO: explain that we'll occasionally have to provide some intrinsics.
|
||||
|
||||
## Bare Metal Panic
|
||||
|
||||
TODO: expand this
|
||||
|
@ -114,3 +110,10 @@ TODO: expand this
|
|||
* Sending the message also automatically zeroes the output buffer.
|
||||
* View the output within the "Tools" menu, "View Logs...". Note that the Fatal
|
||||
message, if any doesn't get logged.
|
||||
|
||||
TODO: this will probably fail without a `__clzsi2` implementation, which is a
|
||||
good seg for the next section
|
||||
|
||||
## LLVM Intrinsics
|
||||
|
||||
TODO: explain that we'll occasionally have to provide some intrinsics.
|
||||
|
|
|
@ -1,13 +1,258 @@
|
|||
# Fixed Only
|
||||
|
||||
In addition to not having the standard library available, we don't even have a
|
||||
floating point unit available! We can't do floating point math in hardware! We
|
||||
could still do floating point math as software computations if we wanted, but
|
||||
that's a slow, slow thing to do.
|
||||
In addition to not having much of the standard library available, we don't even
|
||||
have a floating point unit available! We can't do floating point math in
|
||||
hardware! We _could_ still do floating point math as pure software computations
|
||||
if we wanted, but that's a slow, slow thing to do.
|
||||
|
||||
Instead let's learn about another way to have fractional values called "Fixed
|
||||
Point"
|
||||
Are there faster ways? It's the same answer as always: "Yes, but not without a
|
||||
tradeoff."
|
||||
|
||||
## Fixed Point
|
||||
The faster way is to represent fractional values using a system called a [Fixed
|
||||
Point Representation](https://en.wikipedia.org/wiki/Fixed-point_arithmetic).
|
||||
What do we trade away? Numeric range.
|
||||
|
||||
TODO: describe fixed point, make some types, do the impls, all that.
|
||||
* Floating point math stores bits for base value and for exponent all according
|
||||
to a single [well defined](https://en.wikipedia.org/wiki/IEEE_754) standard
|
||||
for how such a complicated thing works.
|
||||
* Fixed point math takes a normal integer (either signed or unsigned) and then
|
||||
just "mentally associates" it (so to speak) with a fractional value for its
|
||||
"units". If you have 3 and it's in units of 1/2, then you have 3/2, or 1.5
|
||||
using decimal notation. If your number is 256 and it's in units of 1/256th
|
||||
then the value is 1.0 in decimal notation.
|
||||
|
||||
Floating point math requires dedicated hardware to perform quickly, but it can
|
||||
"trade" precision when it needs to represent extremely large or small values.
|
||||
|
||||
Fixed point math is just integral math, which our GBA is reasonably good at, but
|
||||
because your number is associated with a fixed fraction your results can get out
|
||||
of range very easily.
|
||||
|
||||
## Representing A Fixed Point Value
|
||||
|
||||
So we want to associate our numbers with a mental note of what units they're in:
|
||||
|
||||
* [PhantomData](https://doc.rust-lang.org/core/marker/struct.PhantomData.html)
|
||||
is a type that tells the compiler "please remember this extra type info" when
|
||||
you add it as a field to a struct. It goes away at compile time, so it's
|
||||
perfect for us to use as space for a note to ourselves without causing runtime
|
||||
overhead.
|
||||
* The [typenum](https://crates.io/crates/typenum) crate is the best way to
|
||||
represent a number within a type in Rust. Since our values on the GBA are
|
||||
always specified as a number of fractional bits to count the number as, we can
|
||||
put `typenum` types such as `U8` or `U14` into our `PhantomData` to keep track
|
||||
of what's going on.
|
||||
|
||||
Now, those of you who know me, or perhaps just know my reputation, will of
|
||||
course _immediately_ question what happened to the real Lokathor. I do not care
|
||||
for most crates, and I particularly don't care for using a crate in teaching
|
||||
situations. However, `typenum` has a number of factors on its side that let me
|
||||
suggest it in this situation:
|
||||
|
||||
* It's version 1.10 with a total of 21 versions and nearly 700k downloads, so we
|
||||
can expect that the major troubles have been shaken out and that it will remain
|
||||
fairly stable for quite some time to come.
|
||||
* It has no further dependencies that it's going to drag into the compilation.
|
||||
* It happens all at compile time, so it's not clogging up our actual game with
|
||||
any nonsense.
|
||||
* The (interesting) subject of "how do you do math inside Rust's trait system?" is
|
||||
totally separate from the concern that we're trying to focus on here.
|
||||
|
||||
Therefore, we will consider it acceptable to use this crate.
|
||||
|
||||
Now the `typenum` crate defines a whole lot, but we'll focus down to just a
|
||||
single type at the moment:
|
||||
[UInt](https://docs.rs/typenum/1.10.0/typenum/uint/struct.UInt.html) is a
|
||||
type-level unsigned value. It's like `u8` or `u16`, but while they're types that
|
||||
then have values, each `UInt` construction statically equates to a specific
|
||||
value. Like how the `()` type only has one value, which is also called `()`. In
|
||||
this case, you wrap up `UInt` around smaller `UInt` values and a `B1` or `B0`
|
||||
value to build up the binary number that you want at the type level.
|
||||
|
||||
In other words, instead of writing
|
||||
|
||||
```rust
|
||||
let six = 0b110;
|
||||
```
|
||||
|
||||
We write
|
||||
|
||||
```rust
|
||||
type U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0>;
|
||||
```
|
||||
|
||||
Wild, I know. If you look into the `typenum` crate you can do math and stuff
|
||||
with these type level numbers, and we will a little bit below, but to start off
|
||||
we _just_ need to store one in some `PhantomData`.
|
||||
|
||||
### A struct For Fixed Point
|
||||
|
||||
Our actual type for a fixed point value looks like this:
|
||||
|
||||
```rust
|
||||
use core::marker::PhantomData;
|
||||
use typenum::marker_traits::Unsigned;
|
||||
|
||||
/// Fixed point `T` value with `F` fractional bits.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Fx<T, F: Unsigned> {
|
||||
bits: T,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
```
|
||||
|
||||
This says that `Fx<T,F>` is a generic type that holds some base number type `T`
|
||||
and a `F` type that's marking off how many fractional bits we're using. We only
|
||||
want people giving unsigned type-level values for the `PhantomData` type, so we
|
||||
use the trait bound `F: Unsigned`.
|
||||
|
||||
We use
|
||||
[repr(transparent)](https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md)
|
||||
here to ensure that `Fx` will always be treated just like the base type in the
|
||||
final program (in terms of bit pattern and ABI).
|
||||
|
||||
If you go and check, this is _basically_ how the existing general purpose crates
|
||||
for fixed point math represent their numbers. They're a little fancier about it
|
||||
because they have to cover every case, and we only have to cover our GBA case.
|
||||
|
||||
That's quite a bit to type though. We probably want to make a few type aliases
|
||||
for things to be easier to look at. Unfortunately there's [no standard
|
||||
notation](https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Notation) for how
|
||||
you write a fixed point type. We also have to limit ourselves to what's valid
|
||||
for use in a Rust type too. I like the `fx` thing, so we'll use that for signed
|
||||
and then `fxu` if we need an unsigned value.
|
||||
|
||||
```rust
|
||||
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
||||
pub type fx8_8 = Fx<i16,U8>;
|
||||
```
|
||||
|
||||
Rust will complain about having `non_camel_case_types`, and you can shut that
|
||||
warning up by putting an `#[allow(non_camel_case_types)]` attribute on the type
|
||||
alias directly, or you can use `#![allow(non_camel_case_types)]` at the very top
|
||||
of the module to shut up that warning for the whole module (which is what I
|
||||
did).
|
||||
|
||||
## Constructing A Fixed Point Value
|
||||
|
||||
So how do we actually _make_ one of these values? Well, we can always just wrap or unwrap any value in our `Fx` type:
|
||||
|
||||
```rust
|
||||
impl<T, F: Unsigned> Fx<T, F> {
|
||||
/// Uses the provided value directly.
|
||||
pub fn from_raw(r: T) -> Self {
|
||||
Fx {
|
||||
num: r,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Unwraps the inner value.
|
||||
pub fn into_raw(self) -> T {
|
||||
self.num
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'd like to use the `From` trait of course, but it was giving me some trouble, i
|
||||
think because of the orphan rule. Oh well.
|
||||
|
||||
If we want to be particular to the fact that these are supposed to be
|
||||
_numbers_... that gets tricky. Rust is actually quite bad at being generic about
|
||||
number types. You can use the [num](https://crates.io/crates/num) crate, or you
|
||||
can just use a macro and invoke it once per type. Guess what we're gonna do.
|
||||
|
||||
```rust
|
||||
macro_rules! fixed_point_methods {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Fx<$t, F> {
|
||||
/// Gives 0 for this type.
|
||||
pub fn zero() -> Self {
|
||||
Fx {
|
||||
num: 0,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the smallest positive non-zero value.
|
||||
pub fn precision() -> Self {
|
||||
Fx {
|
||||
num: 1,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a value with the integer part shifted into place.
|
||||
pub fn from_int_part(i: $t) -> Self {
|
||||
Fx {
|
||||
num: i << F::to_u8(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_methods! {u8}
|
||||
fixed_point_methods! {i8}
|
||||
fixed_point_methods! {i16}
|
||||
fixed_point_methods! {u16}
|
||||
fixed_point_methods! {i32}
|
||||
fixed_point_methods! {u32}
|
||||
```
|
||||
|
||||
Now _you'd think_ that those can all be `const`, but at the moment you can't
|
||||
have a `const` function with a bound on any trait other than `Sized`, so they
|
||||
have to be normal functions.
|
||||
|
||||
Also, we're doing something a little interesting there with `from_int_part`. We
|
||||
can take our `F` type and get it as a value instead of a type using `to_u8`.
|
||||
|
||||
## Casting Values
|
||||
|
||||
Next, once we have a value in one type, we need to be able to move it into
|
||||
another type. A particular `Fx` type is a base number type and a fractional
|
||||
count, so there's two ways we might want to move it.
|
||||
|
||||
For casting the base type it's a little weird. Because there's so many number
|
||||
types, and we can't be generic about them when using `as`, we'd have to make
|
||||
like 30 functions (6 base number types we're using, times 5 target number types
|
||||
you could cast to). Instead, we'll write it just once, and let the user pass a
|
||||
closure that does the cast.
|
||||
|
||||
We can put this as part of the basic impl block that `from_raw` and `into_raw`
|
||||
are part of. If can avoid having code inside a macro we'll do it just because
|
||||
macros are messy.
|
||||
|
||||
```rust
|
||||
/// Casts the base type, keeping the fractional bit quantity the same.
|
||||
pub fn cast_inner<Z, C: Fn(T) -> Z>(self, op: C) -> Fx<Z, F> {
|
||||
Fx {
|
||||
num: op(self.num),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's... not the best to have to pass in the casting operation like that.
|
||||
Hopefully we won't have to use it much.
|
||||
|
||||
Also we might want to change the amount of fractional bits in a number. Oh,
|
||||
gosh, this one is kinda complicated.
|
||||
|
||||
## Addition / Subtraction
|
||||
|
||||
## Multiplication / Division
|
||||
|
||||
## Trigonometry
|
||||
|
||||
## Just Using A Crate
|
||||
|
||||
If you feel too intimidated by all of this then I'll suggest to you that the
|
||||
[fixed](https://crates.io/crates/fixed) crate seems to be the best crate
|
||||
available for fixed point math.
|
||||
|
||||
_I have not tested its use on the GBA myself_.
|
||||
|
||||
It's just my recommendation from looking at the docs of the various options
|
||||
available.
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Newtype
|
||||
|
||||
TODO: we've already used newtype twice by now (fixed point values and volatile
|
||||
addresses), so we need to adjust how we start this section.
|
||||
|
||||
There's a great Zero Cost abstraction that we'll be using a lot that you might
|
||||
not already be familiar with: we're talking about the "Newtype Pattern"!
|
||||
|
||||
|
@ -27,13 +30,13 @@ cost at compile time.
|
|||
pub struct PixelColor(u16);
|
||||
```
|
||||
|
||||
TODO: we've already talked about repr(transparent) by now
|
||||
|
||||
Ah, except that, as I'm sure you remember from [The
|
||||
Rustonomicon](https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent)
|
||||
(and from [the
|
||||
RFC](https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md)
|
||||
too, of course), if we have a single field struct that's sometimes different
|
||||
from having just the bare value, so we should be using `#[repr(transparent)]`
|
||||
with our newtypes.
|
||||
(and from the RFC too, of course), if we have a single field struct that's
|
||||
sometimes different from having just the bare value, so we should be using
|
||||
`#[repr(transparent)]` with our newtypes.
|
||||
|
||||
```rust
|
||||
#[repr(transparent)]
|
||||
|
|
|
@ -20,12 +20,13 @@ macro_rules! const_assert {
|
|||
};
|
||||
}
|
||||
|
||||
/// Constructs an RGB value with a `const_assert!` that the input is in range.
|
||||
#[macro_export]
|
||||
macro_rules! const_rgb {
|
||||
($r:expr, $g:expr, $b:expr) => {{
|
||||
const_assert!($r);
|
||||
const_assert!($g);
|
||||
const_assert!($b);
|
||||
const_assert!($r <= 31);
|
||||
const_assert!($g <= 31);
|
||||
const_assert!($b <= 31);
|
||||
Color::new($r, $g, $b)
|
||||
}};
|
||||
}
|
||||
|
|
38
src/builtins.rs
Normal file
38
src/builtins.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
//! The module to provide "builtin" functions that LLVM expects.
|
||||
//!
|
||||
//! You shouldn't need to call anything in here yourself, it just has to be in
|
||||
//! the translation unit and LLVM will find it.
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn __clzsi2(mut x: usize) -> usize {
|
||||
let mut y: usize;
|
||||
let mut n: usize = 32;
|
||||
y = x >> 16;
|
||||
if y != 0 {
|
||||
n = n - 16;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8;
|
||||
if y != 0 {
|
||||
n = n - 8;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4;
|
||||
if y != 0 {
|
||||
n = n - 4;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2;
|
||||
if y != 0 {
|
||||
n = n - 2;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1;
|
||||
if y != 0 {
|
||||
n - 2
|
||||
} else {
|
||||
n - x
|
||||
}
|
||||
}
|
|
@ -39,4 +39,3 @@ impl<T> VolatilePtr<T> {
|
|||
}
|
||||
|
||||
// TODO: kill all this with fire
|
||||
|
||||
|
|
86
src/fixed.rs
Normal file
86
src/fixed.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
//! Module for fixed point math types and operations.
|
||||
|
||||
use core::{convert::From, marker::PhantomData};
|
||||
use typenum::{marker_traits::Unsigned, U8};
|
||||
|
||||
/// Fixed point `T` value with `F` fractional bits.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Fx<T, F: Unsigned> {
|
||||
num: T,
|
||||
phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<T, F: Unsigned> Fx<T, F> {
|
||||
/// Uses the provided value directly.
|
||||
pub fn from_raw(r: T) -> Self {
|
||||
Fx {
|
||||
num: r,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Unwraps the inner value.
|
||||
pub fn into_raw(self) -> T {
|
||||
self.num
|
||||
}
|
||||
|
||||
/// Casts the base type, keeping the fractional bit quantity the same.
|
||||
pub fn cast_inner<Z, C: Fn(T) -> Z>(self, op: C) -> Fx<Z, F> {
|
||||
Fx {
|
||||
num: op(self.num),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fixed_point_methods {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Fx<$t, F> {
|
||||
/// Gives 0 for this type.
|
||||
pub fn zero() -> Self {
|
||||
Fx {
|
||||
num: 0,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the smallest positive non-zero value.
|
||||
pub fn precision() -> Self {
|
||||
Fx {
|
||||
num: 1,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a value with the integer part shifted into place.
|
||||
pub fn from_int_part(i: $t) -> Self {
|
||||
Fx {
|
||||
num: i << F::to_u8(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives the raw inner value.
|
||||
pub fn into_inner(&self) -> $t {
|
||||
self.num
|
||||
}
|
||||
|
||||
/// Changes the fractional bit quantity, keeping the base type the same.
|
||||
pub fn change_bit_quantity<N: Unsigned>(&self) -> Fx<$t, N> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_methods! {u8}
|
||||
fixed_point_methods! {i8}
|
||||
fixed_point_methods! {i16}
|
||||
fixed_point_methods! {u16}
|
||||
fixed_point_methods! {i32}
|
||||
fixed_point_methods! {u32}
|
||||
|
||||
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
||||
pub type fx8_8 = Fx<i16, U8>;
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
// TODO(lokathor): IO Register newtypes.
|
||||
|
||||
use gba_proc_macro::{newtype, register_bit};
|
||||
use gba_proc_macro::register_bit;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -25,9 +25,10 @@ use super::*;
|
|||
pub const DISPCNT: VolatilePtr<u16> = VolatilePtr(0x400_0000 as *mut u16);
|
||||
|
||||
newtype!(
|
||||
/// A newtype over the various display control options that you have on a GBA.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
DisplayControlSetting,
|
||||
u16,
|
||||
"A newtype over the various display control options that you have on a GBA."
|
||||
u16
|
||||
);
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
@ -412,10 +413,14 @@ pub enum TriBool {
|
|||
Plus = 1,
|
||||
}
|
||||
|
||||
newtype!(KeyInputSetting, u16, "A newtype over the key input state of the GBA");
|
||||
newtype! {
|
||||
/// Records a particular key press combination.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
KeyInput, u16
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl KeyInputSetting {
|
||||
impl KeyInput {
|
||||
register_bit!(A_BIT, u16, 1, a_pressed);
|
||||
register_bit!(B_BIT, u16, 1 << 1, b_pressed);
|
||||
register_bit!(SELECT_BIT, u16, 1 << 2, select_pressed);
|
||||
|
@ -428,8 +433,8 @@ impl KeyInputSetting {
|
|||
register_bit!(L_BIT, u16, 1 << 9, l_pressed);
|
||||
|
||||
/// Takes the difference between these keys and another set of keys.
|
||||
pub fn difference(self, other: KeyInputSetting) -> KeyInputSetting {
|
||||
KeyInputSetting(self.0 ^ other.0)
|
||||
pub fn difference(self, other: Self) -> Self {
|
||||
KeyInput(self.0 ^ other.0)
|
||||
}
|
||||
|
||||
/// Gives the arrow pad value as a tribool, with Plus being increased column
|
||||
|
@ -458,11 +463,11 @@ impl KeyInputSetting {
|
|||
}
|
||||
|
||||
/// Gets the current state of the keys
|
||||
pub fn key_input() -> KeyInputSetting {
|
||||
pub fn key_input() -> KeyInput {
|
||||
// Note(Lokathor): The 10 used bits are "low when pressed" style, but the 6
|
||||
// unused bits are always low, so we XOR with this mask to get a result where
|
||||
// the only active bits are currently pressed keys.
|
||||
unsafe { KeyInputSetting(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||
unsafe { KeyInput(KEYINPUT.read() ^ 0b0000_0011_1111_1111) }
|
||||
}
|
||||
|
||||
/// Key Interrupt Control
|
||||
|
|
58
src/lib.rs
58
src/lib.rs
|
@ -1,12 +1,12 @@
|
|||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(not(test), feature(asm))]
|
||||
#![warn(missing_docs)]
|
||||
//#![allow(clippy::cast_lossless)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
#![deny(clippy::float_arithmetic)]
|
||||
|
||||
//! This crate helps you write GBA ROMs.
|
||||
//!
|
||||
//! # SAFETY POLICY
|
||||
//! ## SAFETY POLICY
|
||||
//!
|
||||
//! Some parts of this crate are safe wrappers around unsafe operations. This is
|
||||
//! good, and what you'd expect from a Rust crate.
|
||||
|
@ -16,21 +16,55 @@
|
|||
//!
|
||||
//! **Do not** use this crate in programs that aren't running on the GBA. If you
|
||||
//! do, it's a giant bag of Undefined Behavior.
|
||||
//!
|
||||
//! # TESTING POLICY
|
||||
//!
|
||||
//! It is the intent of the crate authors that as much of the crate as possible
|
||||
//! be written so that you can use `cargo test` for at least some parts of your
|
||||
//! code without everything exploding instantly. To that end, where possible we
|
||||
//! attempt to use `cfg` flags to make things safe for `cargo test`. Hopefully
|
||||
//! we got it all.
|
||||
|
||||
pub mod core_extras;
|
||||
pub(crate) use crate::core_extras::*;
|
||||
/// Assists in defining a newtype wrapper over some base type.
|
||||
///
|
||||
/// Note that rustdoc and derives are all the "meta" stuff, so you can write all
|
||||
/// of your docs and derives in front of your newtype in the same way you would
|
||||
/// for a normal struct. Then the inner type to be wrapped it name.
|
||||
///
|
||||
/// The macro _assumes_ that you'll be using it to wrap zero safe numeric types,
|
||||
/// so it automatically provides a `const fn` method for `new` that just wraps
|
||||
/// `0`. If this is not desired you can add `, no frills` to the invocation.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// newtype! {
|
||||
/// /// Records a particular key press combination.
|
||||
/// #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
/// KeyInput, u16
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! newtype {
|
||||
($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
pub struct $new_name($old_name);
|
||||
impl $new_name {
|
||||
/// A `const` "zero value" constructor
|
||||
pub const fn new() -> Self {
|
||||
$new_name(0)
|
||||
}
|
||||
}
|
||||
};
|
||||
($(#[$attr:meta])* $new_name:ident, $old_name:ident, no frills) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
pub struct $new_name($old_name);
|
||||
};
|
||||
}
|
||||
|
||||
pub mod builtins;
|
||||
|
||||
pub mod fixed;
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub mod bios;
|
||||
|
||||
pub mod core_extras;
|
||||
pub(crate) use crate::core_extras::*;
|
||||
|
||||
pub mod io_registers;
|
||||
|
||||
pub mod video_ram;
|
||||
|
|
Loading…
Reference in a new issue