2018-12-17 16:01:23 +11:00
|
|
|
# Object Attribute Memory (OAM)
|
|
|
|
|
|
|
|
* **Address Span:** `0x700_0000` to `0x700_03FF` (1k)
|
|
|
|
|
|
|
|
The Object Attribute Memory has a 32-bit bus and no default wait, but suffers
|
|
|
|
from the "you might have to wait if the display controller is looking at it"
|
|
|
|
rule. You cannot write individual bytes to OAM at all, but that's not really a
|
|
|
|
problem because all the fields of the data types within OAM are either `i16` or
|
|
|
|
`u16` anyway.
|
|
|
|
|
|
|
|
Object attribute memory is the wildest yet: it conceptually contains two types
|
|
|
|
of things, but they're _interlaced_ with each other all the way through.
|
|
|
|
|
|
|
|
Now, [GBATEK](http://problemkaputt.de/gbatek.htm#lcdobjoamattributes) and
|
|
|
|
[CowByte](https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm#OAM%20(sprites))
|
|
|
|
doesn't quite give names to the two data types here.
|
|
|
|
[TONC](https://www.coranac.com/tonc/text/regobj.htm#sec-oam) calls them
|
|
|
|
`OBJ_ATTR` and `OBJ_AFFINE`, but we'll be giving them names fitting with the
|
|
|
|
Rust naming convention. Just know that if you try to talk about it with others
|
|
|
|
they might not be using the same names. In Rust terms their layout would look
|
|
|
|
like this:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct ObjectAttributes {
|
|
|
|
attr0: u16,
|
|
|
|
attr1: u16,
|
|
|
|
attr2: u16,
|
|
|
|
filler: i16,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct AffineMatrix {
|
|
|
|
filler0: [u16; 3],
|
|
|
|
pa: i16,
|
|
|
|
filler1: [u16; 3],
|
|
|
|
pb: i16,
|
|
|
|
filler2: [u16; 3],
|
|
|
|
pc: i16,
|
|
|
|
filler3: [u16; 3],
|
|
|
|
pd: i16,
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
(Note: the `#[repr(C)]` part just means that Rust must lay out the data exactly
|
|
|
|
in the order we specify, which otherwise it is not required to do).
|
|
|
|
|
|
|
|
So, we've got 1024 bytes in OAM and each `ObjectAttributes` value is 8 bytes, so
|
|
|
|
naturally we can support up to 128 objects.
|
|
|
|
|
|
|
|
_At the same time_, we've got 1024 bytes in OAM and each `AffineMatrix` is 32
|
|
|
|
bytes, so we can have 32 of them.
|
|
|
|
|
|
|
|
But, as I said, these things are all _interlaced_ with each other. See how
|
|
|
|
there's "filler" fields in each struct? If we imagine the OAM as being just an
|
|
|
|
array of one type or the other, indexes 0/1/2/3 of the `ObjectAttributes` array
|
|
|
|
would line up with index 0 of the `AffineMatrix` array. It's kinda weird, but
|
|
|
|
that's just how it works. When we setup functions to read and write these values
|
|
|
|
we'll have to be careful with how we do it. We probably _won't_ want to use
|
|
|
|
those representations above, at least not with the `AffineMatrix` type, because
|
|
|
|
they're quite wasteful if you want to store just object attributes or just
|
|
|
|
affine matrices.
|