Quick explanation of Volatile

We actually don't need to bore the reader with how fancy it is, they can go read the source of the crate it they want.
This commit is contained in:
Lokathor 2019-02-09 03:52:52 -07:00
parent 7f27d83b6e
commit 2dcf5bfe18
3 changed files with 48 additions and 1 deletions

View file

@ -3,7 +3,7 @@
* [Development Setup](development-setup.md)
* [Volatile](volatile.md)
* [Hardware Memory Map](hardware-memory-map.md)
* [The Hardware Memory Map](the-hardware-memory-map.md)
* [IO Registers](io-registers.md)
* [Bitmap Video](bitmap-video.md)
* [GBA Assembly](gba-asm.md)

View file

@ -1 +1,48 @@
# Volatile
I know that you just got your first program running and you're probably excited
to learn more about GBA stuff, but first we have to cover a subject that's not
quite GBA specific.
In the `hello_magic.rs` file we had these lines
```rust
(0x600_0000 as *mut u16).offset(120 + 80 * 240).write_volatile(0x001F);
(0x600_0000 as *mut u16).offset(136 + 80 * 240).write_volatile(0x03E0);
(0x600_0000 as *mut u16).offset(120 + 96 * 240).write_volatile(0x7C00);
```
You've probably seen or heard of the
[write](https://doc.rust-lang.org/core/ptr/fn.write.html) function before, but
you'd be excused if you've never heard of its cousin function,
[write_volatile](https://doc.rust-lang.org/core/ptr/fn.write_volatile.html).
What's the difference? Well, when the compiler sees normal reads and writes, it
assumes that those go into plain old memory locations. CPU registers, RAM,
wherever it is that the value's being placed. The compiler assumes that it's
safe to optimize away some of the reads and writes, or maybe issue the reads and
writes in a different order from what you wrote. Normally this is okay, and it's
exactly what we want the compiler to be doing, quietly making things faster for us.
However, some of the time we access values from parts of memory where it's
important that each access happen, and in the exact order that we say. In our
`hello_magic.rs` example, we're writing directly into the video memory of the
display. The compiler sees that the rest of the Rust program never read out of
those locations, so it might think "oh, we can skip those writes, they're
pointless". It doesn't know that we're having a side effect besides just storing
some value at an address.
By declaring a particular read or write to be `volatile` then we can force the
compiler to issue that access. Further, we're guaranteed that all `volatile`
access will happen in exactly the order it appears in the program relative to
other `volatile` access. However, non-volatile access can still be re-ordered
relative to a volatile access. In other words, for parts of the memory that are
volatile, we must _always_ use a volatile read or write for our program to
perform properly.
For exactly this reason, we've got the [voladdress](https://docs.rs/voladdress/)
crate. It used to be part of the GBA crate, but it became big enough to break
out into a stand alone crate. It doesn't even do too much, it just makes it a
lot less error prone to accidentally forget to use volatile with our memory
mapped addresses. We just call `read` and `write` on any `VolAddress` that we
happen to see and the right thing will happen.