mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-25 11:41:30 +11:00
522 lines
30 KiB
HTML
522 lines
30 KiB
HTML
|
<!DOCTYPE HTML>
|
||
|
<html lang="en" class="sidebar-visible no-js">
|
||
|
<head>
|
||
|
<!-- Book generated using mdBook -->
|
||
|
<meta charset="UTF-8">
|
||
|
<title>Volatile Destination - 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"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html" class="active"><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></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-network.html"><strong aria-hidden="true">5.6.</strong> Network</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-ex
|
||
|
</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="#volatile-destination" id="volatile-destination"><h1>Volatile Destination</h1></a>
|
||
|
<p>TODO: replace all this one "the rant" is finalized</p>
|
||
|
<p>There's a reasonable chance that you've never heard of <code>volatile</code> before, so
|
||
|
what's that? Well, it's a term that can be used in more than one context, but
|
||
|
basically it means "get your grubby mitts off my stuff you over-eager compiler".</p>
|
||
|
<a class="header" href="#volatile-memory" id="volatile-memory"><h2>Volatile Memory</h2></a>
|
||
|
<p>The first, and most common, form of volatile thing is volatile memory. Volatile
|
||
|
memory can change without your program changing it, usually because it's not a
|
||
|
location in RAM, but instead some special location that represents an actual
|
||
|
hardware device, or part of a hardware device perhaps. The compiler doesn't know
|
||
|
what's going on in this situation, but when the program is actually run and the
|
||
|
CPU gets an instruction to read or write from that location, instead of just
|
||
|
accessing some place in RAM like with normal memory, it accesses whatever bit of
|
||
|
hardware and does <em>something</em>. The details of that something depend on the
|
||
|
hardware, but what's important is that we need to actually, definitely execute
|
||
|
that read or write instruction.</p>
|
||
|
<p>This is not how normal memory works. Normally when the compiler
|
||
|
sees us write values into variables and read values from variables, it's free to
|
||
|
optimize those expressions and eliminate some of the reads and writes if it can,
|
||
|
and generally try to save us time. Maybe it even knows some stuff about the data
|
||
|
dependencies in our expressions and so it does some of the reads or writes out
|
||
|
of order from what the source says, because the compiler knows that it won't
|
||
|
actually make a difference to the operation of the program. A good and helpful
|
||
|
friend, that compiler.</p>
|
||
|
<p>Volatile memory works almost the opposite way. With volatile memory we
|
||
|
need the compiler to <em>definitely</em> emit an instruction to do a read or write and
|
||
|
they need to happen <em>exactly</em> in the order that we say to do it. Each volatile
|
||
|
read or write might have any sort of side effect that the compiler
|
||
|
doesn't know about, and it shouldn't try to be clever about the optimization. Just do what we
|
||
|
say, please.</p>
|
||
|
<p>In Rust, we don't mark volatile things as being a separate type of thing,
|
||
|
instead we use normal raw pointers and then call the
|
||
|
<a href="https://doc.rust-lang.org/core/ptr/fn.read_volatile.html">read_volatile</a> and
|
||
|
<a href="https://doc.rust-lang.org/core/ptr/fn.write_volatile.html">write_volatile</a>
|
||
|
functions (also available as methods, if you like), which then delegate to the
|
||
|
LLVM
|
||
|
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_load.html">volatile_load</a>
|
||
|
and
|
||
|
<a href="https://doc.rust-lang.org/core/intrinsics/fn.volatile_store.html">volatile_store</a>
|
||
|
intrinsics. In C and C++ you can tag a pointer as being volatile and then any
|
||
|
normal read and write with it becomes the volatile version, but in Rust we have
|
||
|
to remember to use the correct alternate function instead.</p>
|
||
|
<p>I'm told by the experts that this makes for a cleaner and saner design from a
|
||
|
<em>language design</em> perspective, but it really kinda screws us when doing low
|
||
|
level code. References, both mutable and shared, aren't volatile, so they
|
||
|
compile into normal reads and writes. This means we can't do anything we'd
|
||
|
normally do in Rust that utilizes references of any kind. Volatile blocks of
|
||
|
memory can't use normal <code>.iter()</code> or <code>.iter_mut()</code> based iteration (which give
|
||
|
<code>&T</code> or <code>&mut T</code>), and they also can't use normal <code>Index</code> and <code>IndexMut</code> sugar
|
||
|
like <code>a + x[i]</code> or <code>x[i] = 7</code>.</p>
|
||
|
<p>Unlike with normal raw pointers, this pain point never goes away. There's no way
|
||
|
to abstract over the difference with Rust as it exists now, you'd need to
|
||
|
actually adjust the core language by adding an additional pointer type (<code>*vol T</code>) and possibly a reference type to go with it (<code>&vol T</code>) to get the right
|
||
|
semantics. And then you'd need an <code>IndexVol</code> trait, and you'd need
|
||
|
<code>.iter_vol()</code>, and so on for every other little thing. It would be a lot of
|
||
|
work, and the Rust developers just aren't interested in doing all that for such
|
||
|
a limited portion of their user population. We'll just have to deal with not
|
||
|
having any syntax sugar.</p>
|
||
|
<a class="header" href="#volatileptr" id="volatileptr"><h3>VolatilePtr</h3></a>
|
||
|
<p>No syntax sugar doesn't mean we can't at least make things a little easier for
|
||
|
ourselves. Enter the <code>VolatilePtr<T></code> type, which is a newtype over a <code>*mut T</code>.
|
||
|
One of those "manual" newtypes I mentioned where we can't use our nice macro.</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||
|
#[repr(transparent)]
|
||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||
|
#}</code></pre></pre>
|
||
|
<p>Obviously we want to be able to read and write:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
impl<T> VolatilePtr<T> {
|
||
|
/// Performs a `read_volatile`.
|
||
|
pub unsafe fn read(self) -> T {
|
||
|
self.0.read_volatile()
|
||
|
}
|
||
|
|
||
|
/// Performs a `write_volatile`.
|
||
|
pub unsafe fn write(self, data: T) {
|
||
|
self.0.write_volatile(data);
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>And we want a way to jump around when we do have volatile memory that's in
|
||
|
blocks. This is where we can get ourselves into some trouble if we're not
|
||
|
careful. We have to decide between
|
||
|
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.offset">offset</a> and
|
||
|
<a href="https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset">wrapping_offset</a>.
|
||
|
The difference is that <code>offset</code> optimizes better, but also it can be Undefined
|
||
|
Behavior if the result is not "in bounds or one byte past the end of the same
|
||
|
allocated object". I asked <a href="https://github.com/ubsan">ubsan</a> (who is the expert
|
||
|
that you should always listen to on matters like this) what that means exactly
|
||
|
when memory mapped hardware is involved (since we never allocated anything), and
|
||
|
the answer was that you <em>can</em> use an <code>offset</code> in statically memory mapped
|
||
|
situations like this as long as you don't use it to jump to the address of
|
||
|
something that Rust itself allocated at some point. Cool, we all like being able
|
||
|
to use the one that optimizes better. Unfortunately, the downside to using
|
||
|
<code>offset</code> instead of <code>wrapping_offset</code> is that with <code>offset</code>, it's Undefined
|
||
|
Behavior <em>simply to calculate the out of bounds result</em> (with <code>wrapping_offset</code>
|
||
|
it's not Undefined Behavior until you <em>use</em> the out of bounds result). We'll
|
||
|
have to be quite careful when we're using <code>offset</code>.</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
/// Performs a normal `offset`.
|
||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||
|
VolatilePtr(self.0.offset(count))
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>Now, one thing of note is that doing the <code>offset</code> isn't <code>const</code>. The math for it
|
||
|
is something that's possible to do in a <code>const</code> way of course, but Rust
|
||
|
basically doesn't allow you to fiddle raw pointers much during <code>const</code> right
|
||
|
now. Maybe in the future that will improve.</p>
|
||
|
<p>If we did want to have a <code>const</code> function for finding the correct address within
|
||
|
a volatile block of memory we'd have to do all the math using <code>usize</code> values,
|
||
|
and then cast that value into being a pointer once we were done. It'd look
|
||
|
something like this:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
const fn address_index<T>(address: usize, index: usize) -> usize {
|
||
|
address + (index * std::mem::size_of::<T>())
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>But, back to methods for <code>VolatilePtr</code>, well we sometimes want to be able to
|
||
|
cast a <code>VolatilePtr</code> between pointer types. Since we won't be able to do that
|
||
|
with <code>as</code>, we'll have to write a method for it:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
/// Performs a cast into some new pointer type.
|
||
|
pub fn cast<Z>(self) -> VolatilePtr<Z> {
|
||
|
VolatilePtr(self.0 as *mut Z)
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<a class="header" href="#volatile-iterating" id="volatile-iterating"><h3>Volatile Iterating</h3></a>
|
||
|
<p>How about that <code>Iterator</code> stuff I said we'd be missing? We can actually make
|
||
|
<em>an</em> Iterator available, it's just not the normal "iterate by shared reference
|
||
|
or unique reference" Iterator. Instead, it's more like a "throw out a series of
|
||
|
<code>VolatilePtr</code> values" style Iterator. Other than that small difference it's
|
||
|
totally normal, and we'll be able to use map and skip and take and all those
|
||
|
neat methods.</p>
|
||
|
<p>So how do we make this thing we need? First we check out the <a href="https://doc.rust-lang.org/core/iter/index.html#implementing-iterator">Implementing
|
||
|
Iterator</a>
|
||
|
section in the core documentation. It says we need a struct for holding the
|
||
|
iterator state. Right-o, probably something like this:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||
|
pub struct VolatilePtrIter<T> {
|
||
|
vol_ptr: VolatilePtr<T>,
|
||
|
slots: usize,
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>And then we just implement
|
||
|
<a href="https://doc.rust-lang.org/core/iter/trait.Iterator.html">core::iter::Iterator</a>
|
||
|
on that struct. Wow, that's quite the trait though! Don't worry, we only need to
|
||
|
implement two small things and then the rest of it comes free as a bunch of
|
||
|
default methods.</p>
|
||
|
<p>So, the code that we <em>want</em> to write looks like this:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
impl<T> Iterator for VolatilePtrIter<T> {
|
||
|
type Item = VolatilePtr<T>;
|
||
|
|
||
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
||
|
if self.slots > 0 {
|
||
|
let out = Some(self.vol_ptr);
|
||
|
self.slots -= 1;
|
||
|
self.vol_ptr = unsafe { self.vol_ptr.offset(1) };
|
||
|
out
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>Except we <em>can't</em> write that code. What? The problem is that we used
|
||
|
<code>derive(Clone, Copy</code> on <code>VolatilePtr</code>. Because of a quirk in how <code>derive</code> works,
|
||
|
this means <code>VolatilePtr<T></code> will only be <code>Copy</code> if the <code>T</code> is <code>Copy</code>, <em>even
|
||
|
though the pointer itself is always <code>Copy</code> regardless of what it points to</em>.
|
||
|
Ugh, terrible. We've got three basic ways to handle this:</p>
|
||
|
<ul>
|
||
|
<li>Make the <code>Iterator</code> implementation be for <code><T:Clone></code>, and then hope that we
|
||
|
always have types that are <code>Clone</code>.</li>
|
||
|
<li>Hand implement every trait we want <code>VolatilePtr</code> (and <code>VolatilePtrIter</code>) to
|
||
|
have so that we can override the fact that <code>derive</code> is basically broken in
|
||
|
this case.</li>
|
||
|
<li>Make <code>VolatilePtr</code> store a <code>usize</code> value instead of a pointer, and then cast
|
||
|
it to <code>*mut T</code> when we actually need to read and write. This would require us
|
||
|
to also store a <code>PhantomData<T></code> so that the type of the address is tracked
|
||
|
properly, which would make it a lot more verbose to construct a <code>VolatilePtr</code>
|
||
|
value.</li>
|
||
|
</ul>
|
||
|
<p>None of those options are particularly appealing. I guess we'll do the first one
|
||
|
because it's the least amount of up front trouble, and I don't <em>think</em> we'll
|
||
|
need to be iterating non-Clone values. All we do to pick that option is add the
|
||
|
bound to the very start of the <code>impl</code> block, where we introduce the <code>T</code>:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
impl<T: Clone> Iterator for VolatilePtrIter<T> {
|
||
|
type Item = VolatilePtr<T>;
|
||
|
|
||
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
||
|
if self.slots > 0 {
|
||
|
let out = Some(self.vol_ptr.clone());
|
||
|
self.slots -= 1;
|
||
|
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
|
||
|
out
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>What's going on here? Okay so our iterator has a number of slots that it'll go
|
||
|
over, and then when it's out of slots it starts producing <code>None</code> forever. That's
|
||
|
actually pretty simple. We're also masking some unsafety too. In this case,
|
||
|
we'll rely on the person who made the <code>VolatilePtrIter</code> to have selected the
|
||
|
correct number of slots. This gives us a new method for <code>VolatilePtr</code>:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
pub unsafe fn iter_slots(self, slots: usize) -> VolatilePtrIter<T> {
|
||
|
VolatilePtrIter {
|
||
|
vol_ptr: self,
|
||
|
slots,
|
||
|
}
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>With this design, making the <code>VolatilePtrIter</code> at the start is <code>unsafe</code> (we have
|
||
|
to trust the caller that the right number of slots exists), and then using it
|
||
|
after that is totally safe (if the right number of slots was given we'll never
|
||
|
screw up our end of it).</p>
|
||
|
<a class="header" href="#volatileptr-formatting" id="volatileptr-formatting"><h3>VolatilePtr Formatting</h3></a>
|
||
|
<p>Also, just as a little bonus that we probably won't use, we could enable our new
|
||
|
pointer type to be formatted as a pointer value.</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
impl<T> core::fmt::Pointer for VolatilePtr<T> {
|
||
|
/// Formats exactly like the inner `*mut T`.
|
||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||
|
write!(f, "{:p}", self.0)
|
||
|
}
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<p>Neat!</p>
|
||
|
<a class="header" href="#volatileptr-complete" id="volatileptr-complete"><h3>VolatilePtr Complete</h3></a>
|
||
|
<p>That was a lot of small code blocks, let's look at it all put together:</p>
|
||
|
<pre><pre class="playpen"><code class="language-rust">
|
||
|
# #![allow(unused_variables)]
|
||
|
#fn main() {
|
||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||
|
#[repr(transparent)]
|
||
|
pub struct VolatilePtr<T>(pub *mut T);
|
||
|
impl<T> VolatilePtr<T> {
|
||
|
pub unsafe fn read(self) -> T {
|
||
|
self.0.read_volatile()
|
||
|
}
|
||
|
pub unsafe fn write(self, data: T) {
|
||
|
self.0.write_volatile(data);
|
||
|
}
|
||
|
pub unsafe fn offset(self, count: isize) -> Self {
|
||
|
VolatilePtr(self.0.offset(count))
|
||
|
}
|
||
|
pub fn cast<Z>(self) -> VolatilePtr<Z> {
|
||
|
VolatilePtr(self.0 as *mut Z)
|
||
|
}
|
||
|
pub unsafe fn iter_slots(self, slots: usize) -> VolatilePtrIter<T> {
|
||
|
VolatilePtrIter {
|
||
|
vol_ptr: self,
|
||
|
slots,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
impl<T> core::fmt::Pointer for VolatilePtr<T> {
|
||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||
|
write!(f, "{:p}", self.0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||
|
pub struct VolatilePtrIter<T> {
|
||
|
vol_ptr: VolatilePtr<T>,
|
||
|
slots: usize,
|
||
|
}
|
||
|
impl<T: Clone> Iterator for VolatilePtrIter<T> {
|
||
|
type Item = VolatilePtr<T>;
|
||
|
fn next(&mut self) -> Option<VolatilePtr<T>> {
|
||
|
if self.slots > 0 {
|
||
|
let out = Some(self.vol_ptr.clone());
|
||
|
self.slots -= 1;
|
||
|
self.vol_ptr = unsafe { self.vol_ptr.clone().offset(1) };
|
||
|
out
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#}</code></pre></pre>
|
||
|
<a class="header" href="#volatile-asm" id="volatile-asm"><h2>Volatile ASM</h2></a>
|
||
|
<p>In addition to some memory locations being volatile, it's also possible for
|
||
|
inline assembly to be declared volatile. This is basically the same idea, "hey
|
||
|
just do what I'm telling you, don't get smart about it".</p>
|
||
|
<p>Normally when you have some <code>asm!</code> it's basically treated like a function,
|
||
|
there's inputs and outputs and the compiler will try to optimize it so that if
|
||
|
you don't actually use the outputs it won't bother with doing those
|
||
|
instructions. However, <code>asm!</code> is basically a pure black box, so the compiler
|
||
|
doesn't know what's happening inside at all, and it can't see if there's any
|
||
|
important side effects going on.</p>
|
||
|
<p>An example of an important side effect that doesn't have output values would be
|
||
|
putting the CPU into a low power state while we want for the next VBlank. This
|
||
|
lets us save quite a bit of battery power. It requires some setup to be done
|
||
|
safely (otherwise the GBA won't ever actually wake back up from the low power
|
||
|
state), but the <code>asm!</code> you use once you're ready is just a single instruction
|
||
|
with no return value. The compiler can't tell what's going on, so you just have
|
||
|
to say "do it anyway".</p>
|
||
|
|
||
|
</main>
|
||
|
|
||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
|
<!-- Mobile navigation buttons -->
|
||
|
|
||
|
<a rel="prev" href="../01-quirks/02-fixed_only.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/04-newtype.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/02-fixed_only.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/04-newtype.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>
|