mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-24 03:11:29 +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,
|
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.
|
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
|
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
|
nowhere, we read various technical manuals and guides ourselves and then
|
||||||
distilled those works oriented around C and C++ into a book for Rust.
|
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 {
|
macro_rules! fixed_point_methods {
|
||||||
($t:ident) => {
|
($t:ident) => {
|
||||||
impl<F: Unsigned> Fx<$t, F> {
|
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.
|
/// Gives the smallest positive non-zero value.
|
||||||
pub fn precision() -> Self {
|
pub fn precision() -> Self {
|
||||||
Fx {
|
Fx {
|
||||||
|
@ -185,7 +177,7 @@ macro_rules! fixed_point_methods {
|
||||||
/// Makes a value with the integer part shifted into place.
|
/// Makes a value with the integer part shifted into place.
|
||||||
pub fn from_int_part(i: $t) -> Self {
|
pub fn from_int_part(i: $t) -> Self {
|
||||||
Fx {
|
Fx {
|
||||||
num: i << F::to_u8(),
|
num: i << F::U8,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,28 +193,34 @@ fixed_point_methods! {i32}
|
||||||
fixed_point_methods! {u32}
|
fixed_point_methods! {u32}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now _you'd think_ that those can all be `const`, but at the moment you can't
|
Now _you'd think_ that those can be `const`, but at the moment you can't have a
|
||||||
have a `const` function with a bound on any trait other than `Sized`, so they
|
`const` function with a bound on any trait other than `Sized`, so they have to
|
||||||
have to be normal functions.
|
be normal functions.
|
||||||
|
|
||||||
Also, we're doing something a little interesting there with `from_int_part`. We
|
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
|
Next, once we have a value in one base type we will need to be able to move it
|
||||||
another type. A particular `Fx` type is a base number type and a fractional
|
into another base type. Unfortunately this means we gotta use the `as` operator,
|
||||||
count, so there's two ways we might want to move it.
|
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
|
We could let the user use `into_raw`, cast, and then do `from_raw`, but that's
|
||||||
types, and we can't be generic about them when using `as`, we'd have to make
|
error prone because they might change the fractional bit count accidentally.
|
||||||
like 30 functions (6 base number types we're using, times 5 target number types
|
This means that we have to write a function that does the casting while
|
||||||
you could cast to). Instead, we'll write it just once, and let the user pass a
|
perfectly preserving the fractional bit quantity. If we wrote one function for
|
||||||
closure that does the cast.
|
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`
|
This code goes outside the macro. I want to avoid too much code in the macro if
|
||||||
are part of. If can avoid having code inside a macro we'll do it just because
|
we can, it's a little easier to cope with I think.
|
||||||
macros are messy.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
/// Casts the base type, keeping the fractional bit quantity the same.
|
/// 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.
|
It's horrible and ugly, but Rust is just bad at numbers sometimes.
|
||||||
Hopefully we won't have to use it much.
|
|
||||||
|
|
||||||
Also we might want to change the amount of fractional bits in a number. Oh,
|
## Adjusting Fractional Part
|
||||||
gosh, this one is kinda complicated.
|
|
||||||
|
|
||||||
## 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
|
## Trigonometry
|
||||||
|
|
||||||
|
TODO: look up tables! arcbits!
|
||||||
|
|
||||||
## Just Using A Crate
|
## Just Using A Crate
|
||||||
|
|
||||||
If you feel too intimidated by all of this then I'll suggest to you that the
|
If, after seeing all that, and seeing that I still didn't even cover every
|
||||||
[fixed](https://crates.io/crates/fixed) crate seems to be the best crate
|
possible trait impl that you might want for all the possible types... if after
|
||||||
available for fixed point math.
|
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_.
|
_I have not tested its use on the GBA myself_.
|
||||||
|
|
||||||
It's just my recommendation from looking at the docs of the various options
|
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
|
//! whatever value is necessary for that function). Some functions also perform
|
||||||
//! necessary checks to save you from yourself, such as not dividing by zero.
|
//! 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.
|
/// (`swi 0x00`) SoftReset the device.
|
||||||
///
|
///
|
||||||
/// This function does not ever return.
|
/// This function does not ever return.
|
||||||
|
@ -175,17 +180,21 @@ pub fn vblank_interrupt_wait() {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) {
|
pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) {
|
||||||
assert!(denominator != 0);
|
assert!(denominator != 0);
|
||||||
let div_out: i32;
|
if cfg!(test) {
|
||||||
let rem_out: i32;
|
(numerator / denominator, numerator % denominator)
|
||||||
unsafe {
|
} else {
|
||||||
asm!(/* ASM */ "swi 0x06"
|
let div_out: i32;
|
||||||
:/* OUT */ "={r0}"(div_out), "={r1}"(rem_out)
|
let rem_out: i32;
|
||||||
:/* INP */ "{r0}"(numerator), "{r1}"(denominator)
|
unsafe {
|
||||||
:/* CLO */ "r3"
|
asm!(/* ASM */ "swi 0x06"
|
||||||
:/* OPT */
|
:/* 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.
|
/// 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.
|
//! Module for fixed point math types and operations.
|
||||||
|
|
||||||
use core::{convert::From, marker::PhantomData};
|
use core::{
|
||||||
use typenum::{marker_traits::Unsigned, U8};
|
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.
|
/// Fixed point `T` value with `F` fractional bits.
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
@ -21,6 +24,7 @@ impl<T, F: Unsigned> Fx<T, F> {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unwraps the inner value.
|
/// Unwraps the inner value.
|
||||||
pub fn into_raw(self) -> T {
|
pub fn into_raw(self) -> T {
|
||||||
self.num
|
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 {
|
macro_rules! fixed_point_methods {
|
||||||
($t:ident) => {
|
($t:ident) => {
|
||||||
impl<F: Unsigned> Fx<$t, F> {
|
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.
|
/// Gives the smallest positive non-zero value.
|
||||||
pub fn precision() -> Self {
|
pub fn precision() -> Self {
|
||||||
Fx {
|
Fx {
|
||||||
|
@ -57,19 +103,22 @@ macro_rules! fixed_point_methods {
|
||||||
/// Makes a value with the integer part shifted into place.
|
/// Makes a value with the integer part shifted into place.
|
||||||
pub fn from_int_part(i: $t) -> Self {
|
pub fn from_int_part(i: $t) -> Self {
|
||||||
Fx {
|
Fx {
|
||||||
num: i << F::to_u8(),
|
num: i << F::U8,
|
||||||
phantom: PhantomData,
|
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.
|
/// Changes the fractional bit quantity, keeping the base type the same.
|
||||||
pub fn change_bit_quantity<N: Unsigned>(&self) -> Fx<$t, N> {
|
pub fn adjust_fractional_bits<Y: Unsigned + IsEqual<F, Output = False>>(self) -> Fx<$t, Y> {
|
||||||
unimplemented!()
|
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! {i32}
|
||||||
fixed_point_methods! {u32}
|
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.
|
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
||||||
pub type fx8_8 = Fx<i16, U8>;
|
pub type fx8_8 = Fx<i16, U8>;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#![cfg_attr(not(test), no_std)]
|
#![no_std]
|
||||||
#![cfg_attr(not(test), feature(asm))]
|
#![feature(asm)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![allow(clippy::cast_lossless)]
|
#![allow(clippy::cast_lossless)]
|
||||||
#![deny(clippy::float_arithmetic)]
|
#![deny(clippy::float_arithmetic)]
|
||||||
|
@ -59,7 +59,6 @@ pub mod builtins;
|
||||||
|
|
||||||
pub mod fixed;
|
pub mod fixed;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
pub mod bios;
|
pub mod bios;
|
||||||
|
|
||||||
pub mod core_extras;
|
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