mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-26 04:01:31 +11:00
699 lines
38 KiB
HTML
699 lines
38 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="sidebar-visible no-js">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>Fixed Only - Rust GBA Guide</title>
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
<meta name="description" content="">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="theme-color" content="#ffffff" />
|
|
|
|
<link rel="shortcut icon" href="../favicon.png">
|
|
<link rel="stylesheet" href="../css/variables.css">
|
|
<link rel="stylesheet" href="../css/general.css">
|
|
<link rel="stylesheet" href="../css/chrome.css">
|
|
<link rel="stylesheet" href="../css/print.css" media="print">
|
|
|
|
<!-- Fonts -->
|
|
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
<link rel="stylesheet" href="../highlight.css">
|
|
<link rel="stylesheet" href="../tomorrow-night.css">
|
|
<link rel="stylesheet" href="../ayu-highlight.css">
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
|
|
|
|
|
</head>
|
|
<body class="light">
|
|
<!-- Provide site root to javascript -->
|
|
<script type="text/javascript">var path_to_root = "../";</script>
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
<script type="text/javascript">
|
|
try {
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
}
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
}
|
|
} catch (e) { }
|
|
</script>
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
<script type="text/javascript">
|
|
var theme;
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
if (theme === null || theme === undefined) { theme = 'light'; }
|
|
document.body.className = theme;
|
|
document.querySelector('html').className = theme + ' js';
|
|
</script>
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
<script type="text/javascript">
|
|
var html = document.querySelector('html');
|
|
var sidebar = 'hidden';
|
|
if (document.body.clientWidth >= 1080) {
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
sidebar = sidebar || 'visible';
|
|
}
|
|
html.classList.remove('sidebar-visible');
|
|
html.classList.add("sidebar-" + sidebar);
|
|
</script>
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
<ol class="chapter"><li><a href="../00-introduction/00-index.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><ol class="section"><li><a href="../00-introduction/01-requirements.html"><strong aria-hidden="true">1.1.</strong> Reader Requirements</a></li><li><a href="../00-introduction/02-goals_and_style.html"><strong aria-hidden="true">1.2.</strong> Book Goals and Style</a></li><li><a href="../00-introduction/03-development-setup.html"><strong aria-hidden="true">1.3.</strong> Development Setup</a></li><li><a href="../00-introduction/04-hello-magic.html"><strong aria-hidden="true">1.4.</strong> Hello, Magic</a></li><li><a href="../00-introduction/05-help_and_resources.html"><strong aria-hidden="true">1.5.</strong> Help and Resources</a></li></ol></li><li><a href="../01-quirks/00-index.html"><strong aria-hidden="true">2.</strong> Quirks</a></li><li><ol class="section"><li><a href="../01-quirks/01-no_std.html"><strong aria-hidden="true">2.1.</strong> No Std</a></li><li><a href="../01-quirks/02-fixed_only.html" class="active"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="../01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li><li><ol class="section"><li><a href="../05-examples/01-hello_magic.html"><strong aria-hidden="true">6.1.</strong> hello_magic</a></li><li><a href="../05-examples/02-hello_world.html"><strong aria-hidden="true">6.2.</strong> hello_world</a></li><li><a href="../05-examples/03-light_cycle.html"><strong aria-hidden="true">6.3.</strong> light_cycle</a></li><li><a href="../05-examples/04-bg_demo.html"><strong aria-hidden="true">6.4.</strong> bg_demo</a></li></ol></li></ol>
|
|
</nav>
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
<div class="page">
|
|
|
|
<div id="menu-bar" class="menu-bar">
|
|
<div id="menu-bar-sticky-container">
|
|
<div class="left-buttons">
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
<i class="fa fa-bars"></i>
|
|
</button>
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
<i class="fa fa-paint-brush"></i>
|
|
</button>
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
</ul>
|
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<h1 class="menu-title">Rust GBA Guide</h1>
|
|
|
|
<div class="right-buttons">
|
|
<a href="../print.html" title="Print this book" aria-label="Print this book">
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</form>
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
<ul id="searchresults">
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
<script type="text/javascript">
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
});
|
|
</script>
|
|
|
|
<div id="content" class="content">
|
|
<main>
|
|
<a class="header" href="#fixed-only" id="fixed-only"><h1>Fixed Only</h1></a>
|
|
<p>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 <em>could</em> still do floating point math as pure software computations
|
|
if we wanted, but that's a slow, slow thing to do.</p>
|
|
<p>Are there faster ways? It's the same answer as always: "Yes, but not without a
|
|
tradeoff."</p>
|
|
<p>The faster way is to represent fractional values using a system called a <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic">Fixed
|
|
Point Representation</a>.
|
|
What do we trade away? Numeric range.</p>
|
|
<ul>
|
|
<li>Floating point math stores bits for base value and for exponent all according
|
|
to a single <a href="https://en.wikipedia.org/wiki/IEEE_754">well defined</a> standard
|
|
for how such a complicated thing works.</li>
|
|
<li>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.</li>
|
|
</ul>
|
|
<p>Floating point math requires dedicated hardware to perform quickly, but it can
|
|
"trade" precision when it needs to represent extremely large or small values.</p>
|
|
<p>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.</p>
|
|
<a class="header" href="#representing-a-fixed-point-value" id="representing-a-fixed-point-value"><h2>Representing A Fixed Point Value</h2></a>
|
|
<p>So we want to associate our numbers with a mental note of what units they're in:</p>
|
|
<ul>
|
|
<li><a href="https://doc.rust-lang.org/core/marker/struct.PhantomData.html">PhantomData</a>
|
|
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.</li>
|
|
<li>The <a href="https://crates.io/crates/typenum">typenum</a> 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 <code>typenum</code> types such as <code>U8</code> or <code>U14</code> into our <code>PhantomData</code> to keep track
|
|
of what's going on.</li>
|
|
</ul>
|
|
<p>Now, those of you who know me, or perhaps just know my reputation, will of
|
|
course <em>immediately</em> 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, <code>typenum</code> has a number of factors on its side that let me
|
|
suggest it in this situation:</p>
|
|
<ul>
|
|
<li>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.</li>
|
|
<li>It has no further dependencies that it's going to drag into the compilation.</li>
|
|
<li>It happens all at compile time, so it's not clogging up our actual game with
|
|
any nonsense.</li>
|
|
<li>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.</li>
|
|
</ul>
|
|
<p>Therefore, we will consider it acceptable to use this crate.</p>
|
|
<p>Now the <code>typenum</code> crate defines a whole lot, but we'll focus down to just a
|
|
single type at the moment:
|
|
<a href="https://docs.rs/typenum/1.10.0/typenum/uint/struct.UInt.html">UInt</a> is a
|
|
type-level unsigned value. It's like <code>u8</code> or <code>u16</code>, but while they're types that
|
|
then have values, each <code>UInt</code> construction statically equates to a specific
|
|
value. Like how the <code>()</code> type only has one value, which is also called <code>()</code>. In
|
|
this case, you wrap up <code>UInt</code> around smaller <code>UInt</code> values and a <code>B1</code> or <code>B0</code>
|
|
value to build up the binary number that you want at the type level.</p>
|
|
<p>In other words, instead of writing</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
let six = 0b110;
|
|
#}</code></pre></pre>
|
|
<p>We write</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
type U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0>;
|
|
#}</code></pre></pre>
|
|
<p>Wild, I know. If you look into the <code>typenum</code> crate you can do math and stuff
|
|
with these type level numbers, and we will a little bit below, but to start off
|
|
we <em>just</em> need to store one in some <code>PhantomData</code>.</p>
|
|
<a class="header" href="#a-struct-for-fixed-point" id="a-struct-for-fixed-point"><h3>A struct For Fixed Point</h3></a>
|
|
<p>Our actual type for a fixed point value looks like this:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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>,
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>This says that <code>Fx<T,F></code> is a generic type that holds some base number type <code>T</code>
|
|
and a <code>F</code> type that's marking off how many fractional bits we're using. We only
|
|
want people giving unsigned type-level values for the <code>PhantomData</code> type, so we
|
|
use the trait bound <code>F: Unsigned</code>.</p>
|
|
<p>We use
|
|
<a href="https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md">repr(transparent)</a>
|
|
here to ensure that <code>Fx</code> will always be treated just like the base type in the
|
|
final program (in terms of bit pattern and ABI).</p>
|
|
<p>If you go and check, this is <em>basically</em> 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.</p>
|
|
<p>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 <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Notation">no standard
|
|
notation</a> 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 <code>fx</code> thing, so we'll use that for signed
|
|
and then <code>fxu</code> if we need an unsigned value.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![allow(unused_variables)]
|
|
#fn main() {
|
|
/// Alias for an `i16` fixed point value with 8 fractional bits.
|
|
pub type fx8_8 = Fx<i16,U8>;
|
|
#}</code></pre></pre>
|
|
<p>Rust will complain about having <code>non_camel_case_types</code>, and you can shut that
|
|
warning up by putting an <code>#[allow(non_camel_case_types)]</code> attribute on the type
|
|
alias directly, or you can use <code>#![allow(non_camel_case_types)]</code> at the very top
|
|
of the module to shut up that warning for the whole module (which is what I
|
|
did).</p>
|
|
<a class="header" href="#constructing-a-fixed-point-value" id="constructing-a-fixed-point-value"><h2>Constructing A Fixed Point Value</h2></a>
|
|
<p>So how do we actually <em>make</em> one of these values? Well, we can always just wrap or unwrap any value in our <code>Fx</code> type:</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>I'd like to use the <code>From</code> trait of course, but it was giving me some trouble, i
|
|
think because of the orphan rule. Oh well.</p>
|
|
<p>If we want to be particular to the fact that these are supposed to be
|
|
<em>numbers</em>... that gets tricky. Rust is actually quite bad at being generic about
|
|
number types. You can use the <a href="https://crates.io/crates/num">num</a> crate, or you
|
|
can just use a macro and invoke it once per type. Guess what we're gonna do.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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}
|
|
#}</code></pre></pre>
|
|
<p>Now <em>you'd think</em> that those can be <code>const</code>, but at the moment you can't have a
|
|
<code>const</code> function with a bound on any trait other than <code>Sized</code>, so they have to
|
|
be normal functions.</p>
|
|
<p>Also, we're doing something a little interesting there with <code>from_int_part</code>. We
|
|
can take our <code>F</code> 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).</p>
|
|
<a class="header" href="#casting-base-values" id="casting-base-values"><h2>Casting Base Values</h2></a>
|
|
<p>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 <code>as</code> operator,
|
|
which requires a concrete source type and a concrete destination type. There's
|
|
no easy way for us to make it generic here.</p>
|
|
<p>We could let the user use <code>into_raw</code>, cast, and then do <code>from_raw</code>, 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.</p>
|
|
<p>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.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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,
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>It's horrible and ugly, but Rust is just bad at numbers sometimes.</p>
|
|
<a class="header" href="#adjusting-fractional-part" id="adjusting-fractional-part"><h2>Adjusting Fractional Part</h2></a>
|
|
<p>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.</p>
|
|
<p>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 <code>$t</code> as our type. We
|
|
could alternately put a similar function outside the macro and be generic on <code>T</code>
|
|
having the left and right shift operators by using a <code>where</code> clause. As much as
|
|
I'd like to avoid too much code being generated by macro, I'd <em>even more</em> like
|
|
to avoid generic code with huge and complicated trait bounds. It comes down to
|
|
style, and you gotta decide for yourself.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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,
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>There's a few things at work. First, we introduce <code>Y</code> as the target number of
|
|
fractional bits, and we <em>also</em> 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?</p>
|
|
<p>Now, once we're sure that the current bits and target bits aren't the same, we
|
|
compute <code>target - start</code>, 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 <em>must</em> be negative because we
|
|
eliminated 0 as a possibility using the type-level operator, so we shift to the
|
|
right by the negative value.</p>
|
|
<a class="header" href="#addition-subtraction-shifting-negative-comparisons" id="addition-subtraction-shifting-negative-comparisons"><h2>Addition, Subtraction, Shifting, Negative, Comparisons</h2></a>
|
|
<p>From here on we're getting help from <a href="https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/">this blog
|
|
post</a> by <a href="https://spin.atomicobject.com/author/vranish/">Job
|
|
Vranish</a>, so thank them if you
|
|
learn something.</p>
|
|
<p>I might have given away the game a bit with those <code>derive</code> 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).</p>
|
|
<p>This code can go outside the macro, with sufficient trait bounds.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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,
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>The bound on <code>T</code> makes it so that <code>Fx<T, F></code> can be added any time that <code>T</code> can
|
|
be added to its own type with itself as the output. We can use the exact same
|
|
pattern for <code>Sub</code>, <code>Shl</code>, <code>Shr</code>, and <code>Neg</code>. With enough trait bounds, we can do
|
|
anything!</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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,
|
|
}
|
|
}
|
|
}
|
|
#}</code></pre></pre>
|
|
<p>Unfortunately, for <code>Shl</code> and <code>Shr</code> 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 <code>u32</code> is fine. We can always add more later if
|
|
we need.</p>
|
|
<p>We could also implement <code>BitAnd</code>, <code>BitOr</code>, <code>BitXor</code>, and <code>Not</code>, 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, <code>Rem</code> 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 <code>Div</code> (which maybe we don't need, but it's complex enough I should
|
|
show it instead of letting people guess).</p>
|
|
<p><strong>Note:</strong> In addition to the various <code>Op</code> traits, there's also <code>OpAssign</code>
|
|
variants. Each <code>OpAssign</code> is the same as <code>Op</code>, but takes <code>&mut self</code> instead of
|
|
<code>self</code> and then modifies in place instead of producing a fresh value. In other
|
|
words, if you want both <code>+</code> and <code>+=</code> you'll need to do the <code>AddAssign</code> trait
|
|
too. It's not the worst thing to just write <code>a = a+b</code>, so I won't bother with
|
|
showing all that here. It's pretty easy to figure out for yourself if you want.</p>
|
|
<a class="header" href="#multiplication" id="multiplication"><h2>Multiplication</h2></a>
|
|
<p>This is where things get more interesting. When we have two numbers <code>A</code> and <code>B</code>
|
|
they really stand for <code>(a*f)</code> and <code>(b*f)</code>. If we write <code>A*B</code> then we're really
|
|
writing <code>(a*f)*(b*f)</code>, which can be rewritten as <code>(a*b)*2f</code>, and now it's
|
|
obvious that we have one more <code>f</code> than we wanted to have. We have to do the
|
|
multiply of the inner value and then divide out the <code>f</code>. We divide by <code>1 << bit_count</code>, so if we have 8 fractional bits we'll divide by 256.</p>
|
|
<p>The catch is that, when we do the multiply we're <em>extremely</em> 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).</p>
|
|
<p>Naturally, any signed value has to be cast up to <code>i32</code> and any unsigned value
|
|
has to be cast up to <code>u32</code>, so we'll have to handle those separately.</p>
|
|
<p>Also, instead of doing an <em>actual</em> divide we can right-shift by the correct
|
|
number of bits to achieve the same effect. <em>Except</em> 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 <em>this</em> 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).</p>
|
|
<p>Also, again signed values can be annoying, because if the value <em>just happens</em>
|
|
to be <code>i32::MIN</code> then when you negate it you'll have... <em>still</em> a negative
|
|
value. I'm not 100% on this, but I think the correct thing to do at that point
|
|
is to give <code>$t::MIN</code> as out output num value.</p>
|
|
<p>Did you get all that? Good, because this is involves casting, we will need to
|
|
implement it three times, which calls for another macro.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#division" id="division"><h2>Division</h2></a>
|
|
<p>Division is similar to multiplication, but reversed. Which makes sense. This
|
|
time <code>A/B</code> gives <code>(a*f)/(b*f)</code> which is <code>a/b</code>, one <em>less</em> <code>f</code> than we were
|
|
after.</p>
|
|
<p>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.</p>
|
|
<p>The snag here is that the GBA has no division or remainder. Instead, the GBA has
|
|
a BIOS function you can call to do <code>i32/i32</code> division.</p>
|
|
<p>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 <code>i32</code> <em>after the multiply</em> so
|
|
that we can cast it to <code>i32</code>, call the BIOS function that only works on <code>i32</code>
|
|
values, and cast it back to its actual type.</p>
|
|
<ul>
|
|
<li>If you have a u8 you're always okay, even with 8 floating bits.</li>
|
|
<li>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.</li>
|
|
<li>If you have a u32 you're probably going to be in trouble all the time.</li>
|
|
</ul>
|
|
<p>So... ugh, there's not much we can do about this. For now we'll just have to
|
|
suffer some.</p>
|
|
<p>// TODO: find a numerics book that tells us how to do <code>u32/u32</code> divisions.</p>
|
|
<pre><pre class="playpen"><code class="language-rust">
|
|
# #![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}
|
|
#}</code></pre></pre>
|
|
<a class="header" href="#trigonometry" id="trigonometry"><h2>Trigonometry</h2></a>
|
|
<p>TODO: look up tables! arcbits!</p>
|
|
<a class="header" href="#just-using-a-crate" id="just-using-a-crate"><h2>Just Using A Crate</h2></a>
|
|
<p>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 <a href="https://crates.io/crates/fixed">fixed</a> crate seems to
|
|
be the best crate available for fixed point math.</p>
|
|
<p><em>I have not tested its use on the GBA myself</em>.</p>
|
|
<p>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.</p>
|
|
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
<a rel="prev" href="../01-quirks/01-no_std.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
|
|
|
|
<a rel="next" href="../01-quirks/03-volatile_destination.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
|
<a href="../01-quirks/01-no_std.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
<i class="fa fa-angle-left"></i>
|
|
</a>
|
|
|
|
|
|
|
|
<a href="../01-quirks/03-volatile_destination.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
<i class="fa fa-angle-right"></i>
|
|
</a>
|
|
|
|
</nav>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
|
|
|
|
|
</body>
|
|
</html>
|