mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
fixed point stuff
This commit is contained in:
parent
58d739dd9e
commit
4d27005150
|
@ -32,6 +32,10 @@ Reference](https://doc.rust-lang.org/nightly/reference/introduction.html) to see
|
|||
if they cover it. You can mostly ignore that big scary red banner at the top,
|
||||
things are a lot better documented than they make it sound.
|
||||
|
||||
If you need help trying to fiddle your math down as hard as you can, there are
|
||||
resources such as the [Bit Twiddling
|
||||
Hacks](https://graphics.stanford.edu/~seander/bithacks.html) page.
|
||||
|
||||
As to GBA related lore, Ketsuban and I didn't magically learn this all from
|
||||
nowhere, we read various technical manuals and guides ourselves and then
|
||||
distilled those works oriented around C and C++ into a book for Rust.
|
||||
|
|
|
@ -166,14 +166,6 @@ can just use a macro and invoke it once per type. Guess what we're gonna do.
|
|||
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 {
|
||||
|
@ -185,7 +177,7 @@ macro_rules! fixed_point_methods {
|
|||
/// Makes a value with the integer part shifted into place.
|
||||
pub fn from_int_part(i: $t) -> Self {
|
||||
Fx {
|
||||
num: i << F::to_u8(),
|
||||
num: i << F::U8,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -201,28 +193,34 @@ 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.
|
||||
Now _you'd think_ that those can 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`.
|
||||
can take our `F` type and get its constant value. There's other associated
|
||||
constants if we want it in other types, and also non-const methods if you wanted
|
||||
that for some reason (maybe passing it as a closure function? dunno).
|
||||
|
||||
## Casting Values
|
||||
## Casting Base 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.
|
||||
Next, once we have a value in one base type we will need to be able to move it
|
||||
into another base type. Unfortunately this means we gotta use the `as` operator,
|
||||
which requires a concrete source type and a concrete destination type. There's
|
||||
no easy way for us to make it generic here.
|
||||
|
||||
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 could let the user use `into_raw`, cast, and then do `from_raw`, but that's
|
||||
error prone because they might change the fractional bit count accidentally.
|
||||
This means that we have to write a function that does the casting while
|
||||
perfectly preserving the fractional bit quantity. If we wrote one function for
|
||||
each conversion it'd be like 30 different possible casts (6 base types that we
|
||||
support, and then 5 possible target types). Instead, we'll write it just once in
|
||||
a way that takes a closure, and let the user pass a closure that does the cast.
|
||||
The compiler should merge it all together quite nicely for us once optimizations
|
||||
kick in.
|
||||
|
||||
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.
|
||||
This code goes outside the macro. I want to avoid too much code in the macro if
|
||||
we can, it's a little easier to cope with I think.
|
||||
|
||||
```rust
|
||||
/// Casts the base type, keeping the fractional bit quantity the same.
|
||||
|
@ -234,25 +232,317 @@ macros are messy.
|
|||
}
|
||||
```
|
||||
|
||||
It's... not the best to have to pass in the casting operation like that.
|
||||
Hopefully we won't have to use it much.
|
||||
It's horrible and ugly, but Rust is just bad at numbers sometimes.
|
||||
|
||||
Also we might want to change the amount of fractional bits in a number. Oh,
|
||||
gosh, this one is kinda complicated.
|
||||
## Adjusting Fractional Part
|
||||
|
||||
## Addition / Subtraction
|
||||
In addition to the base value we might want to change our fractional bit
|
||||
quantity. This is actually easier that it sounds, but it also requires us to be
|
||||
tricky with the generics. We can actually use some typenum type level operators
|
||||
here.
|
||||
|
||||
## Multiplication / Division
|
||||
This code goes inside the macro: we need to be able to use the left shift and
|
||||
right shift, which is easiest when we just use the macro's `$t` as our type. We
|
||||
could alternately put a similar function outside the macro and be generic on `T`
|
||||
having the left and right shift operators by using a `where` clause. As much as
|
||||
I'd like to avoid too much code being generated by macro, I'd _even more_ like
|
||||
to avoid generic code with huge and complicated trait bounds. It comes down to
|
||||
style, and you gotta decide for yourself.
|
||||
|
||||
```rust
|
||||
/// Changes the fractional bit quantity, keeping the base type the same.
|
||||
pub fn adjust_fractional_bits<Y: Unsigned + IsEqual<F, Output = False>>(self) -> Fx<$t, Y> {
|
||||
let leftward_movement: i32 = Y::to_i32() - F::to_i32();
|
||||
Fx {
|
||||
num: if leftward_movement > 0 {
|
||||
self.num << leftward_movement
|
||||
} else {
|
||||
self.num >> (-leftward_movement)
|
||||
},
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There's a few things at work. First, we introduce `Y` as the target number of
|
||||
fractional bits, and we _also_ limit it that the target bits quantity can't be
|
||||
the same as we already have using a type-level operator. If it's the same as we
|
||||
started with, why are you doing the cast at all?
|
||||
|
||||
Now, once we're sure that the current bits and target bits aren't the same, we
|
||||
compute `target - start`, and call this our "leftward movement". Example: if
|
||||
we're targeting 8 bits and we're at 4 bits, we do 8-4 and get +4 as our leftward
|
||||
movement. If the leftward_movement is positive we naturally shift our current
|
||||
value to the left. If it's not positive then it _must_ be negative because we
|
||||
eliminated 0 as a possibility using the type-level operator, so we shift to the
|
||||
right by the negative value.
|
||||
|
||||
## Addition, Subtraction, Shifting, Negative, Comparisons
|
||||
|
||||
From here on we're getting help from [this blog
|
||||
post](https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/) by [Job
|
||||
Vranish](https://spin.atomicobject.com/author/vranish/), so thank them if you
|
||||
learn something.
|
||||
|
||||
I might have given away the game a bit with those `derive` traits on our fixed
|
||||
point type. For a fair number of operations you can use the normal form of the
|
||||
op on the inner bits as long as the fractional parts have the same quantity.
|
||||
This includes equality and ordering (which we derived) as well as addition,
|
||||
subtraction, and bit shifting (which we need to do ourselves).
|
||||
|
||||
This code can go outside the macro, with sufficient trait bounds.
|
||||
|
||||
```rust
|
||||
impl<T: Add<Output = T>, F: Unsigned> Add for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Fx<T, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num + rhs.num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The bound on `T` makes it so that `Fx<T, F>` can be added any time that `T` can
|
||||
be added to its own type with itself as the output. We can use the exact same
|
||||
pattern for `Sub`, `Shl`, `Shr`, and `Neg`. With enough trait bounds, we can do
|
||||
anything!
|
||||
|
||||
```rust
|
||||
impl<T: Sub<Output = T>, F: Unsigned> Sub for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: Fx<T, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num - rhs.num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Shl<u32, Output = T>, F: Unsigned> Shl<u32> for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn shl(self, rhs: u32) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num << rhs,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Shr<u32, Output = T>, F: Unsigned> Shr<u32> for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn shr(self, rhs: u32) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num >> rhs,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Neg<Output = T>, F: Unsigned> Neg for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Fx {
|
||||
num: -self.num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Unfortunately, for `Shl` and `Shr` to have as much coverage on our type as it
|
||||
does on the base type (allowing just about any right hand side) we'd have to do
|
||||
another macro, but I think just `u32` is fine. We can always add more later if
|
||||
we need.
|
||||
|
||||
We could also implement `BitAnd`, `BitOr`, `BitXor`, and `Not`, but they don't
|
||||
seem relevent to our fixed point math use, and this section is getting long
|
||||
already. Just use the same general patterns if you want to add it in your own
|
||||
programs. Shockingly, `Rem` also works directly if you want it, though I don't
|
||||
forsee us needing floating point remainder. Also, the GBA can't do hardware
|
||||
division or remainder, and we'll have to work around that below when we
|
||||
implement `Div` (which maybe we don't need, but it's complex enough I should
|
||||
show it instead of letting people guess).
|
||||
|
||||
**Note:** In addition to the various `Op` traits, there's also `OpAssign`
|
||||
variants. Each `OpAssign` is the same as `Op`, but takes `&mut self` instead of
|
||||
`self` and then modifies in place instead of producing a fresh value. In other
|
||||
words, if you want both `+` and `+=` you'll need to do the `AddAssign` trait
|
||||
too. It's not the worst thing to just write `a = a+b`, so I won't bother with
|
||||
showing all that here. It's pretty easy to figure out for yourself if you want.
|
||||
|
||||
## Multiplication
|
||||
|
||||
This is where things get more interesting. When we have two numbers `A` and `B`
|
||||
they really stand for `(a*f)` and `(b*f)`. If we write `A*B` then we're really
|
||||
writing `(a*f)*(b*f)`, which can be rewritten as `(a*b)*2f`, and now it's
|
||||
obvious that we have one more `f` than we wanted to have. We have to do the
|
||||
multiply of the inner value and then divide out the `f`. We divide by `1 <<
|
||||
bit_count`, so if we have 8 fractional bits we'll divide by 256.
|
||||
|
||||
The catch is that, when we do the multiply we're _extremely_ likely to overflow
|
||||
our base type with that multiplication step. Then we do that divide, and now our
|
||||
result is basically nonsense. We can avoid this to some extent by casting up to
|
||||
a higher bit type, doing the multiplication and division at higher precision,
|
||||
and then casting back down. We want as much precision as possible without being
|
||||
too inefficient, so we'll always cast up to 32-bit (on a 64-bit machine you'd
|
||||
cast up to 64-bit instead).
|
||||
|
||||
Naturally, any signed value has to be cast up to `i32` and any unsigned value
|
||||
has to be cast up to `u32`, so we'll have to handle those separately.
|
||||
|
||||
Also, instead of doing an _actual_ divide we can right-shift by the correct
|
||||
number of bits to achieve the same effect. _Except_ when we have a signed value
|
||||
that's negative, because actual division truncates towards zero and
|
||||
right-shifting truncates towards negative infinity. We can get around _this_ by
|
||||
flipping the sign, doing the shift, and flipping the sign again (which sounds
|
||||
silly but it's so much faster than doing an actual division).
|
||||
|
||||
Also, again signed values can be annoying, because if the value _just happens_
|
||||
to be `i32::MIN` then when you negate it you'll have... _still_ a negative
|
||||
value. I'm not 100% on this, but I think the correct thing to do at that point
|
||||
is to give `$t::MIN` as out output num value.
|
||||
|
||||
Did you get all that? Good, because this is involves casting, we will need to
|
||||
implement it three times, which calls for another macro.
|
||||
|
||||
```rust
|
||||
macro_rules! fixed_point_signed_multiply {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let pre_shift = (self.num as i32).wrapping_mul(rhs.num as i32);
|
||||
if pre_shift < 0 {
|
||||
if pre_shift == core::i32::MIN {
|
||||
Fx {
|
||||
num: core::$t::MIN,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
} else {
|
||||
Fx {
|
||||
num: (-((-pre_shift) >> F::U8)) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Fx {
|
||||
num: (pre_shift >> F::U8) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_signed_multiply! {i8}
|
||||
fixed_point_signed_multiply! {i16}
|
||||
fixed_point_signed_multiply! {i32}
|
||||
|
||||
macro_rules! fixed_point_unsigned_multiply {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: ((self.num as u32).wrapping_mul(rhs.num as u32) >> F::U8) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_unsigned_multiply! {u8}
|
||||
fixed_point_unsigned_multiply! {u16}
|
||||
fixed_point_unsigned_multiply! {u32}
|
||||
```
|
||||
|
||||
## Division
|
||||
|
||||
Division is similar to multiplication, but reversed. Which makes sense. This
|
||||
time `A/B` gives `(a*f)/(b*f)` which is `a/b`, one _less_ `f` than we were
|
||||
after.
|
||||
|
||||
As with the multiplication version of things, we have to up-cast our inner value
|
||||
as much a we can before doing the math, to allow for the most precision
|
||||
possible.
|
||||
|
||||
The snag here is that the GBA has no division or remainder. Instead, the GBA has
|
||||
a BIOS function you can call to do `i32/i32` division.
|
||||
|
||||
This is a potential problem for us though. If we have some unsigned value, we
|
||||
need it to fit within the positive space of an `i32` _after the multiply_ so
|
||||
that we can cast it to `i32`, call the BIOS function that only works on `i32`
|
||||
values, and cast it back to its actual type.
|
||||
|
||||
* If you have a u8 you're always okay, even with 8 floating bits.
|
||||
* If you have a u16 you're okay even with a maximum value up to 15 floating
|
||||
bits, but having a maximum value and 16 floating bits makes it break.
|
||||
* If you have a u32 you're probably going to be in trouble all the time.
|
||||
|
||||
So... ugh, there's not much we can do about this. For now we'll just have to
|
||||
suffer some.
|
||||
|
||||
// TODO: find a numerics book that tells us how to do `u32/u32` divisions.
|
||||
|
||||
```rust
|
||||
macro_rules! fixed_point_signed_division {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
||||
Fx {
|
||||
num: divide_result as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_signed_division! {i8}
|
||||
fixed_point_signed_division! {i16}
|
||||
fixed_point_signed_division! {i32}
|
||||
|
||||
macro_rules! fixed_point_unsigned_division {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
||||
Fx {
|
||||
num: divide_result as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_unsigned_division! {u8}
|
||||
fixed_point_unsigned_division! {u16}
|
||||
fixed_point_unsigned_division! {u32}
|
||||
```
|
||||
|
||||
## Trigonometry
|
||||
|
||||
TODO: look up tables! arcbits!
|
||||
|
||||
## 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.
|
||||
If, after seeing all that, and seeing that I still didn't even cover every
|
||||
possible trait impl that you might want for all the possible types... if after
|
||||
all that you feel too intimidated, then I'll cave a bit on your behalf and
|
||||
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.
|
||||
available, if you really wanted to just have a crate for it.
|
||||
|
|
29
src/bios.rs
29
src/bios.rs
|
@ -8,6 +8,11 @@
|
|||
//! whatever value is necessary for that function). Some functions also perform
|
||||
//! necessary checks to save you from yourself, such as not dividing by zero.
|
||||
|
||||
//TODO: ALL functions in this module should have `if cfg!(test)` blocks. The
|
||||
//functions that never return must panic, the functions that return nothing
|
||||
//should just do so, and the math functions should just return the correct math
|
||||
//I guess.
|
||||
|
||||
/// (`swi 0x00`) SoftReset the device.
|
||||
///
|
||||
/// This function does not ever return.
|
||||
|
@ -175,17 +180,21 @@ pub fn vblank_interrupt_wait() {
|
|||
#[inline(always)]
|
||||
pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) {
|
||||
assert!(denominator != 0);
|
||||
let div_out: i32;
|
||||
let rem_out: i32;
|
||||
unsafe {
|
||||
asm!(/* ASM */ "swi 0x06"
|
||||
:/* OUT */ "={r0}"(div_out), "={r1}"(rem_out)
|
||||
:/* INP */ "{r0}"(numerator), "{r1}"(denominator)
|
||||
:/* CLO */ "r3"
|
||||
:/* OPT */
|
||||
);
|
||||
if cfg!(test) {
|
||||
(numerator / denominator, numerator % denominator)
|
||||
} else {
|
||||
let div_out: i32;
|
||||
let rem_out: i32;
|
||||
unsafe {
|
||||
asm!(/* ASM */ "swi 0x06"
|
||||
:/* OUT */ "={r0}"(div_out), "={r1}"(rem_out)
|
||||
:/* INP */ "{r0}"(numerator), "{r1}"(denominator)
|
||||
:/* CLO */ "r3"
|
||||
:/* OPT */
|
||||
);
|
||||
}
|
||||
(div_out, rem_out)
|
||||
}
|
||||
(div_out, rem_out)
|
||||
}
|
||||
|
||||
/// As `div_rem`, keeping only the `div` output.
|
||||
|
|
176
src/fixed.rs
176
src/fixed.rs
|
@ -2,8 +2,11 @@
|
|||
|
||||
//! Module for fixed point math types and operations.
|
||||
|
||||
use core::{convert::From, marker::PhantomData};
|
||||
use typenum::{marker_traits::Unsigned, U8};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Add, Div, Mul, Neg, Shl, Shr, Sub},
|
||||
};
|
||||
use typenum::{consts::False, marker_traits::Unsigned, type_operators::IsEqual, U8};
|
||||
|
||||
/// Fixed point `T` value with `F` fractional bits.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
@ -21,6 +24,7 @@ impl<T, F: Unsigned> Fx<T, F> {
|
|||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps the inner value.
|
||||
pub fn into_raw(self) -> T {
|
||||
self.num
|
||||
|
@ -35,17 +39,59 @@ impl<T, F: Unsigned> Fx<T, F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Add<Output = T>, F: Unsigned> Add for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Fx<T, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num + rhs.num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub<Output = T>, F: Unsigned> Sub for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: Fx<T, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num - rhs.num,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Shl<u32, Output = T>, F: Unsigned> Shl<u32> for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn shl(self, rhs: u32) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num << rhs,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Shr<u32, Output = T>, F: Unsigned> Shr<u32> for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn shr(self, rhs: u32) -> Self::Output {
|
||||
Fx {
|
||||
num: self.num >> rhs,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Neg<Output = T>, F: Unsigned> Neg for Fx<T, F> {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Fx {
|
||||
num: -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 {
|
||||
|
@ -57,19 +103,22 @@ macro_rules! fixed_point_methods {
|
|||
/// Makes a value with the integer part shifted into place.
|
||||
pub fn from_int_part(i: $t) -> Self {
|
||||
Fx {
|
||||
num: i << F::to_u8(),
|
||||
num: i << F::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!()
|
||||
pub fn adjust_fractional_bits<Y: Unsigned + IsEqual<F, Output = False>>(self) -> Fx<$t, Y> {
|
||||
let leftward_movement: i32 = Y::to_i32() - F::to_i32();
|
||||
Fx {
|
||||
num: if leftward_movement > 0 {
|
||||
self.num << leftward_movement
|
||||
} else {
|
||||
self.num >> (-leftward_movement)
|
||||
},
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -82,5 +131,96 @@ fixed_point_methods! {u16}
|
|||
fixed_point_methods! {i32}
|
||||
fixed_point_methods! {u32}
|
||||
|
||||
macro_rules! fixed_point_signed_multiply {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let pre_shift = (self.num as i32).wrapping_mul(rhs.num as i32);
|
||||
if pre_shift < 0 {
|
||||
if pre_shift == core::i32::MIN {
|
||||
Fx {
|
||||
num: core::$t::MIN,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
} else {
|
||||
Fx {
|
||||
num: (-((-pre_shift) >> F::U8)) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Fx {
|
||||
num: (pre_shift >> F::U8) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_signed_multiply! {i8}
|
||||
fixed_point_signed_multiply! {i16}
|
||||
fixed_point_signed_multiply! {i32}
|
||||
|
||||
macro_rules! fixed_point_unsigned_multiply {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Mul for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
Fx {
|
||||
num: ((self.num as u32).wrapping_mul(rhs.num as u32) >> F::U8) as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_unsigned_multiply! {u8}
|
||||
fixed_point_unsigned_multiply! {u16}
|
||||
fixed_point_unsigned_multiply! {u32}
|
||||
|
||||
macro_rules! fixed_point_signed_division {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
||||
Fx {
|
||||
num: divide_result as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_signed_division! {i8}
|
||||
fixed_point_signed_division! {i16}
|
||||
fixed_point_signed_division! {i32}
|
||||
|
||||
macro_rules! fixed_point_unsigned_division {
|
||||
($t:ident) => {
|
||||
impl<F: Unsigned> Div for Fx<$t, F> {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Fx<$t, F>) -> Self::Output {
|
||||
let mul_output: i32 = (self.num as i32).wrapping_mul(1 << F::U8);
|
||||
let divide_result: i32 = crate::bios::div(mul_output, rhs.num as i32);
|
||||
Fx {
|
||||
num: divide_result as $t,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixed_point_unsigned_division! {u8}
|
||||
fixed_point_unsigned_division! {u16}
|
||||
fixed_point_unsigned_division! {u32}
|
||||
|
||||
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
||||
pub type fx8_8 = Fx<i16, U8>;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![cfg_attr(not(test), no_std)]
|
||||
#![cfg_attr(not(test), feature(asm))]
|
||||
#![no_std]
|
||||
#![feature(asm)]
|
||||
#![warn(missing_docs)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
#![deny(clippy::float_arithmetic)]
|
||||
|
@ -59,7 +59,6 @@ pub mod builtins;
|
|||
|
||||
pub mod fixed;
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub mod bios;
|
||||
|
||||
pub mod core_extras;
|
||||
|
|
12
todo_check.bat
Normal file
12
todo_check.bat
Normal file
|
@ -0,0 +1,12 @@
|
|||
@echo off
|
||||
|
||||
echo -------
|
||||
echo -------
|
||||
|
||||
set Wildcard=*.rs
|
||||
|
||||
echo TODOS FOUND:
|
||||
findstr -s -n -i -l "TODO" %Wildcard%
|
||||
|
||||
echo -------
|
||||
echo -------
|
Loading…
Reference in a new issue