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 and CowByte doesn't quite give names to the two data types here. TONC 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:


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