-
-
Notifications
You must be signed in to change notification settings - Fork 52
Description
When modifying OAM for objects I utilize the OBJ_ATTR_ALL.index(x).write()
functions to update specific objects, like so:
OBJ_ATTR_ALL.index(0).write(ObjAttr::new());
I recently noticed weird behavior where my objects would update as expected in dev mode (and lower levels of optimizations (2, 3)) but would stop updating in optimization modes "s" and "z". After digging into the assembly (of which I know little about), I noticed that it would replace the three strh
(Store Half Word) instructions with a __aeabi_memcpy. When the memcpy implementation was present the updates would no longer reflect, I assume because OAM requires half-word writes and memcpy does 8 bit writes.
I was able to get around this by adding a new function onto ObjAttr in the GBA crate as a workaround and inverting the calling convention. This uses explicit write_volatile calls for each u16 in the ObjAttr struct to ensure each is written as is and not optimized away. Using a single write_volatile for the struct results in the same memcpy optimization:
pub struct ObjAttr(pub ObjAttr0, pub ObjAttr1, pub ObjAttr2);
impl ObjAttr {
// Existing logic
#[inline]
pub fn write(&self, addr: VolAddress<ObjAttr, Safe, Safe>) {
unsafe {
let addr: *mut u16 = addr.as_usize() as *mut u16;
core::ptr::write_volatile(addr.add(0), self.0.0);
core::ptr::write_volatile(addr.add(1), self.1.0);
core::ptr::write_volatile(addr.add(2), self.2.0);
}
}
}
Example usage:
ObjAttr::new().write(OBJ_ATTR_ALL.index(0));
I've uploaded a simplified repo with an example, compiling with lower optimization levels results in strh instructions where compiling at higher optimizations results in memcpy.
https://github.com/undecidedapollo/gba-objattr-volatile-write-bug
I'm new to GBA development as a whole, am a relative beginner with rust, and know little about assembly, so I may have some details backwards / making a simple mistake. Thanks for looking into this.