From 96a05f996377c5383716962555a7b20c98762047 Mon Sep 17 00:00:00 2001 From: "DocsBot (from Travis CI)" Date: Fri, 21 Dec 2018 00:30:01 +0000 Subject: [PATCH] Deploy rust-console/gba to github.com/rust-console/gba.git:master --- docs/00-introduction/00-index.html | 2 +- docs/00-introduction/01-requirements.html | 2 +- docs/00-introduction/02-goals_and_style.html | 2 +- .../00-introduction/03-development-setup.html | 2 +- docs/00-introduction/04-hello-magic.html | 2 +- .../05-help_and_resources.html | 16 +- docs/01-quirks/00-index.html | 2 +- docs/01-quirks/01-no_std.html | 8 +- docs/01-quirks/02-fixed_only.html | 508 ++++++- docs/01-quirks/03-volatile_destination.html | 2 +- docs/01-quirks/04-newtype.html | 165 ++- docs/01-quirks/05-const_asserts.html | 319 +++++ docs/02-concepts/00-index.html | 31 +- docs/02-concepts/01-cpu.html | 2 +- docs/02-concepts/02-bios.html | 212 ++- docs/02-concepts/03-wram.html | 25 +- docs/02-concepts/04-io-registers.html | 5 +- docs/02-concepts/05-palram.html | 46 +- docs/02-concepts/06-vram.html | 26 +- docs/02-concepts/07-oam.html | 60 +- docs/02-concepts/08-rom.html | 15 +- docs/02-concepts/09-sram.html | 16 +- docs/03-video/00-index.html | 8 +- docs/03-video/01-rgb15.html | 2 +- docs/03-video/TODO.html | 2 +- docs/04-non-video/00-index.html | 17 +- docs/04-non-video/01-buttons.html | 4 +- docs/04-non-video/02-timers.html | 2 +- docs/04-non-video/03-dma.html | 2 +- docs/04-non-video/04-sound.html | 2 +- docs/04-non-video/05-interrupts.html | 6 +- .../{06-network.html => 06-link_cable.html} | 6 +- docs/04-non-video/07-game_pak.html | 6 +- docs/05-examples/00-index.html | 2 +- docs/05-examples/01-hello_magic.html | 2 +- docs/05-examples/02-hello_world.html | 2 +- docs/05-examples/03-light_cycle.html | 2 +- docs/05-examples/04-bg_demo.html | 2 +- docs/index.html | 2 +- docs/print.html | 1246 +++++++++++++++-- docs/searchindex.js | 2 +- docs/searchindex.json | 2 +- 42 files changed, 2555 insertions(+), 232 deletions(-) create mode 100644 docs/01-quirks/05-const_asserts.html rename docs/04-non-video/{06-network.html => 06-link_cable.html} (77%) diff --git a/docs/00-introduction/00-index.html b/docs/00-introduction/00-index.html index 2ce175c..2a8fd16 100644 --- a/docs/00-introduction/00-index.html +++ b/docs/00-introduction/00-index.html @@ -72,7 +72,7 @@
diff --git a/docs/00-introduction/01-requirements.html b/docs/00-introduction/01-requirements.html index 6533b50..0910ca4 100644 --- a/docs/00-introduction/01-requirements.html +++ b/docs/00-introduction/01-requirements.html @@ -72,7 +72,7 @@
diff --git a/docs/00-introduction/02-goals_and_style.html b/docs/00-introduction/02-goals_and_style.html index ecc7fe2..4ea97b0 100644 --- a/docs/00-introduction/02-goals_and_style.html +++ b/docs/00-introduction/02-goals_and_style.html @@ -72,7 +72,7 @@
diff --git a/docs/00-introduction/03-development-setup.html b/docs/00-introduction/03-development-setup.html index 8f1de5a..fa0c7ae 100644 --- a/docs/00-introduction/03-development-setup.html +++ b/docs/00-introduction/03-development-setup.html @@ -72,7 +72,7 @@
diff --git a/docs/00-introduction/04-hello-magic.html b/docs/00-introduction/04-hello-magic.html index 083a468..9ff8c75 100644 --- a/docs/00-introduction/04-hello-magic.html +++ b/docs/00-introduction/04-hello-magic.html @@ -72,7 +72,7 @@
diff --git a/docs/00-introduction/05-help_and_resources.html b/docs/00-introduction/05-help_and_resources.html index e6a6713..db1f0cd 100644 --- a/docs/00-introduction/05-help_and_resources.html +++ b/docs/00-introduction/05-help_and_resources.html @@ -72,7 +72,7 @@
@@ -156,9 +156,17 @@ cables and all that.

lets you run raw ELF files, which means that you can have full debug symbols available while you're debugging problems.

Information Resources

-

Ketsuban and I didn't magically learn this all from nowhere, we read various -technical manuals and guides ourselves and then distilled the knowledge (usually -oriented towards C and C++) into this book for Rust.

+

First, if I fail to describe something related to Rust, you can always try +checking in The Rust +Reference 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 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.

We have personally used some or all of the following:

  • GBATEK: This is the resource. It diff --git a/docs/01-quirks/00-index.html b/docs/01-quirks/00-index.html index 934930e..cf03b1d 100644 --- a/docs/01-quirks/00-index.html +++ b/docs/01-quirks/00-index.html @@ -72,7 +72,7 @@
    diff --git a/docs/01-quirks/01-no_std.html b/docs/01-quirks/01-no_std.html index 9c60f9d..79e93ce 100644 --- a/docs/01-quirks/01-no_std.html +++ b/docs/01-quirks/01-no_std.html @@ -72,7 +72,7 @@
    @@ -216,8 +216,6 @@ Either a Rust Global Allocator (if practical), which would allow for a lot of 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

      @@ -245,6 +243,10 @@ to send a message line:
    • 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.

    diff --git a/docs/01-quirks/02-fixed_only.html b/docs/01-quirks/02-fixed_only.html index e386376..e32d164 100644 --- a/docs/01-quirks/02-fixed_only.html +++ b/docs/01-quirks/02-fixed_only.html @@ -72,7 +72,7 @@
    @@ -137,14 +137,504 @@

    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.

    -

    Instead let's learn about another way to have fractional values called "Fixed -Point"

    -

    Fixed Point

    -

    TODO: describe fixed point, make some types, do the impls, all that.

    +

    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.

    +

    Are there faster ways? It's the same answer as always: "Yes, but not without a +tradeoff."

    +

    The faster way is to represent fractional values using a system called a Fixed +Point Representation. +What do we trade away? Numeric range.

    +
      +
    • Floating point math stores bits for base value and for exponent all according +to a single well defined 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 +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 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 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

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +let six = 0b110;
    +#}
    +

    We write

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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:

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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) +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 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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +/// 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:

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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 crate, or you +can just use a macro and invoke it once per type. Guess what we're gonna do.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +macro_rules! fixed_point_methods {
    +  ($t:ident) => {
    +    impl<F: Unsigned> Fx<$t, F> {
    +      /// 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::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 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 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 Base Values

    +

    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.

    +

    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.

    +

    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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +  /// 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 horrible and ugly, but Rust is just bad at numbers sometimes.

    +

    Adjusting Fractional Part

    +

    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.

    +

    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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +      /// 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 by Job +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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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!

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +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, 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 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, if you really wanted to just have a crate for it.

    diff --git a/docs/01-quirks/03-volatile_destination.html b/docs/01-quirks/03-volatile_destination.html index e7f977d..b07e635 100644 --- a/docs/01-quirks/03-volatile_destination.html +++ b/docs/01-quirks/03-volatile_destination.html @@ -72,7 +72,7 @@
    diff --git a/docs/01-quirks/04-newtype.html b/docs/01-quirks/04-newtype.html index 2c53885..5c912c5 100644 --- a/docs/01-quirks/04-newtype.html +++ b/docs/01-quirks/04-newtype.html @@ -72,7 +72,7 @@
    @@ -137,6 +137,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"!

    Now, I told you to read the Rust Book before you read this book, and I'm sure @@ -161,39 +163,29 @@ cost at compile time.

    #fn main() { 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 -(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.

    +(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.

    
     # #![allow(unused_variables)]
     #fn main() {
     #[repr(transparent)]
     pub struct PixelColor(u16);
     #}
    -

    Ah, and of course we'll need to make it so you can unwrap the value:

    -
    
    -# #![allow(unused_variables)]
    -#fn main() {
    -#[repr(transparent)]
    -pub struct PixelColor(u16);
    -
    -impl From<PixelColor> for u16 {
    -  fn from(color: PixelColor) -> u16 {
    -    color.0
    -  }
    -}
    -#}

    And then we'll need to do that same thing for every other newtype we want.

    Except there's only two tiny parts that actually differ between newtype declarations: the new name and the base type. All the rest is just the same rote code over and over. Generating piles and piles of boilerplate code? Sounds like a job for a macro to me!

    Making It A Macro

    -

    The most basic version of the macro we want goes like this:

    +

    If you're going to do much with macros you should definitely read through The +Little Book of Rust +Macros, but we won't be +doing too much so you can just follow along here a bit if you like.

    +

    The most basic version of a newtype macro starts like this:

    
     # #![allow(unused_variables)]
     #fn main() {
    @@ -205,69 +197,92 @@ macro_rules! newtype {
       };
     }
     #}
    -

    Except we also want to be able to add attributes (which includes doc comments), -so we upgrade our macro a bit:

    +

    The #[macro_export] makes it exported by the current module (like pub +kinda), and then we have one expansion option that takes an identifier, a ,, +and then a second identifier. The new name is the outer type we'll be using, and +the old name is the inner type that's being wrapped. You'd use our new macro +something like this:

    
     # #![allow(unused_variables)]
     #fn main() {
    -#[macro_export]
    -macro_rules! newtype {
    -  ($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
    -    $(#[$attr])*
    -    #[repr(transparent)]
    -    pub struct $new_name($old_name);
    -  };
    -}
    +newtype! {PixelColorCurly, u16}
    +
    +newtype!(PixelColorParens, u16);
    +
    +newtype![PixelColorBrackets, u16];
     #}
    -

    And we want to automatically add the ability to turn the wrapper type back into -the wrapped type.

    -
    
    -# #![allow(unused_variables)]
    -#fn main() {
    -#[macro_export]
    -macro_rules! newtype {
    -  ($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
    -    $(#[$attr])*
    -    #[repr(transparent)]
    -    pub struct $new_name($old_name);
    -    
    -    impl From<$new_name> for $old_name {
    -      fn from(x: $new_name) -> $old_name {
    -        x.0
    -      }
    -    }
    -  };
    -}
    -#}
    -

    That seems like enough for all of our examples, so we'll stop there. We could -add more things:

    +

    Note that you can invoke the macro with the outermost grouping as any of (), +[], or {}. It makes no particular difference to the macro. Also, that space +in the first version is kinda to show off that you can put white space in +between the macro name and the grouping if you want. The difference is mostly +style, but there are some rules and considerations here:

      -
    • Making the From impl being optional. We'd have to make the newtype -invocation be more complicated somehow, the user puts ", no-unwrap" after the -inner type declaration or something, or something like that.
    • -
    • Allowing for more precise visibility controls on the wrapping type and on the -inner field. This would add a lot of line noise, so we'll just always have our -newtypes be pub.
    • -
    • Allowing for generic newtypes, which might sound silly but that we'll actually -see an example of soon enough. To do this you might think that we can change -the :ident declarations to :ty, but since we're declaring a fresh type not -using an existing type we have to accept it as an :ident. The way you get -around this is with a proc-macro, which is a lot more powerful but which also -requires that you write the proc-macro in an entirely other crate that gets -compiled first. We don't need that much power, so for our examples we'll go -with the macro_rules version and just do it by hand in the few cases where we -need a generic newtype.
    • -
    • Allowing for Deref and DerefMut, which usually defeats the point of doing -the newtype, but maybe sometimes it's the right thing, so if you were going -for the full industrial strength version with a proc-macro and all you might -want to make that part of your optional add-ons as well the same way you might -want optional From. You'd probably want From to be "on by default" and -Deref/DerefMut to be "off by default", but whatever.
    • +
    • If you use curly braces then you must not put a ; after the invocation.
    • +
    • If you use parentheses or brackets then you must put the ; at the end.
    • +
    • Rustfmt cares which you use and formats accordingly: +
        +
      • Curly brace macro use mostly gets treated like a code block.
      • +
      • Parentheses macro use mostly gets treated like a function call.
      • +
      • Bracket macro use mostly gets treated like an array declaration.
      +
    • +
    +

    Upgrade That Macro!

    +

    We also want to be able to add derive stuff and doc comments to our newtype. +Within the context of macro_rules! definitions these are called "meta". Since +we can have any number of them we wrap it all up in a "zero or more" matcher. +Then our macro looks like this:

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +#[macro_export]
    +macro_rules! newtype {
    +  ($(#[$attr:meta])* $new_name:ident, $old_name:ident) => {
    +    $(#[$attr])*
    +    #[repr(transparent)]
    +    pub struct $new_name($old_name);
    +  };
    +}
    +#}
    +

    So now we can write

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +newtype! {
    +  /// Color on the GBA gives 5 bits for each channel, the highest bit is ignored.
    +  #[derive(Debug, Clone, Copy)]
    +  PixelColor, u16
    +}
    +#}
    +

    And that's about all we'll need for the examples.

    As a reminder: remember that macro_rules macros have to appear before they're invoked in your source, so the newtype macro will always have to be at the very top of your file, or if you put it in a module within your project you'll need to declare the module before anything that uses it.

    +

    Potential Homework

    +

    If you wanted to keep going and get really fancy with it, you could potentially +add a lot more:

    +
      +
    • Make a pub const fn new() -> Self method that outputs the base value in a +const way. Combine this with builder style "setter" methods that are also +const and you can get the compiler to do quite a bit of the value building +work at compile time.
    • +
    • Making the macro optionally emit a From impl to unwrap it back into the base +type.
    • +
    • Allow for visibility modifiers to be applied to the inner field and the newly +generated type.
    • +
    • Allowing for generic newtypes. You already saw the need for this once in the +volatile section. Unfortunately, this particular part gets really tricky if +you're using macro_rules!, so you might need to move up to a full +proc_macro. Having a proc_macro isn't bad except that they have to be +defined in a crate of their own and they're compiled before use. You can't +ever use them in the crate that defines them, so we won't be using them in any +of our single file examples.
    • +
    • Allowing for optional Deref and DerefMut of the inner value. This takes +away most all the safety aspect of doing the newtype, but there may be times +for it. As an example, you could make a newtype with a different form of +Display impl that you want to otherwise treat as the base type in all places.
    • +
    @@ -280,7 +295,7 @@ you'll need to declare the module before anything that uses it.

    - @@ -298,7 +313,7 @@ you'll need to declare the module before anything that uses it.

    - diff --git a/docs/01-quirks/05-const_asserts.html b/docs/01-quirks/05-const_asserts.html new file mode 100644 index 0000000..71cf3ed --- /dev/null +++ b/docs/01-quirks/05-const_asserts.html @@ -0,0 +1,319 @@ + + + + + + Const Asserts - Rust GBA Guide + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + + + + + +
    +
    +

    Constant Assertions

    +

    Have you ever wanted to assert things even before runtime? We all have, of +course. Particularly when the runtime machine is a poor little GBA, we'd like to +have the machine doing the compile handle as much checking as possible.

    +

    Enter the static assertions crate, which +provides a way to let you assert on a const expression.

    +

    This is an amazing crate that you should definitely use when you can.

    +

    It's written by Nikolai Vazquez, and they kindly +wrote up a blog +post that +explains the thinking behind it.

    +

    However, I promised that each example would be single file, and I also promised +to explain what's going on as we go, so we'll briefly touch upon giving an +explanation here.

    +

    How We Const Assert

    +

    Alright, as it stands (2018-12-15), we can't use if in a const context.

    +

    Since we can't use if, we can't use a normal assert!. Some day it will be +possible, and a failed assert at compile time will be a compile error and a +failed assert at run time will be a panic and we'll have a nice unified +programming experience. We can add runtime-only assertions by being a little +tricky with the compiler.

    +

    If we write

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +const ASSERT: usize = 0 - 1;
    +#}
    +

    that gives a warning, since the math would underflow. We can upgrade that +warning to a hard error:

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +#[deny(const_err)]
    +const ASSERT: usize = 0 - 1;
    +#}
    +

    And to make our construction reusable we can enable the +underscore_const_names feature +in our program (or library) and then give each such const an underscore for a +name.

    +
    
    +# #![allow(unused_variables)]
    +#![feature(underscore_const_names)]
    +
    +#fn main() {
    +#[deny(const_err)]
    +const _: usize = 0 - 1;
    +#}
    +

    Now we wrap this in a macro where we give a bool expression as input. We +negate the bool then cast it to a usize, meaning that true negates into +false, which becomes 0usize, and then there's no underflow error. Or if the +input was false, it negates into true, then becomes 1usize, and then the +underflow error fires.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +macro_rules! const_assert {
    +  ($condition:expr) => {
    +    #[deny(const_err)]
    +    #[allow(dead_code)]
    +    const ASSERT: usize = 0 - !$condition as usize;
    +  }
    +}
    +#}
    +

    Technically, written like this, the expression can be anything with a +core::ops::Not implementation that can also be as cast into usize. That's +bool, but also basically all the other number types. Since we want to ensure +that we get proper looking type errors when things go wrong, we can use +($condition && true) to enforce that we get a bool (thanks to Talchas for +that particular suggestion).

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +macro_rules! const_assert {
    +  ($condition:expr) => {
    +    #[deny(const_err)]
    +    #[allow(dead_code)]
    +    const _: usize = 0 - !($condition && true) as usize;
    +  }
    +}
    +#}
    +

    Asserting Something

    +

    As an example of how we might use a const_assert, we'll do a demo with colors. +There's a red, blue, and green channel. We store colors in a u16 with 5 bits +for each channel.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +newtype! {
    +  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    +  Color, u16
    +}
    +#}
    +

    And when we're building a color, we're passing in u16 values, but they could +be using more than just 5 bits of space. We want to make sure that each channel +is 31 or less, so we can make a color builder that does a const_assert! on the +value of each channel.

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +macro_rules! rgb {
    +  ($r:expr, $g:expr, $b:expr) => {
    +    {
    +      const_assert!($r <= 31);
    +      const_assert!($g <= 31);
    +      const_assert!($b <= 31);
    +      Color($b << 10 | $g << 5 | $r)
    +    }
    +  }
    +}
    +#}
    +

    And then we can declare some colors

    +
    
    +# #![allow(unused_variables)]
    +#fn main() {
    +const RED: Color = rgb!(31, 0, 0);
    +
    +const BLUE: Color = rgb!(31, 500, 0);
    +#}
    +

    The second one is clearly out of bounds and it fires an error just like we +wanted.

    + +
    + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/02-concepts/00-index.html b/docs/02-concepts/00-index.html index dfd487f..896bbcc 100644 --- a/docs/02-concepts/00-index.html +++ b/docs/02-concepts/00-index.html @@ -72,7 +72,7 @@
    @@ -137,13 +137,38 @@

    Broad Concepts

    +

    The GameBoy Advance sits in a middle place between the chthonic game consoles of +the ancient past and the "small PC in a funny case" consoles of the modern age.

    +

    On the one hand, yeah, you're gonna find a few strange conventions as you learn +all the ropes.

    +

    On the other, at least we're writing in Rust at all, and not having to do all +the assembly by hand.

    +

    This chapter for "concepts" has a section for each part of the GBA's hardware +memory map, going by increasing order of base address value. The sections try to +explain as much as possible while sticking to just the concerns you might have +regarding that part of the memory map.

    +

    For an assessment of how to wrangle all three parts of the video system (PALRAM, +VRAM, and OAM), along with the correct IO registers, into something that shows a +picture, you'll want the Video chapter.

    +

    Similarly, the "IO Registers" part of the GBA actually controls how you interact +with every single bit of hardware connected to the GBA. A full description of +everything is obviously too much for just one section of the book. Instead you +get an overview of general IO register rules and advice. Each particular +register is described in the appropriate sections of either the Video or +Non-Video chapters.

    +

    Bus Size

    +

    TODO: describe this

    +

    Minimum Write Size

    +

    TODO: talk about parts where you can't write one byte at a time

    +

    Volatile or Not?

    +

    TODO: discuss what memory should be used volatile style and what can be used normal style.