Implements runtime support functions according to ARM's AEABI. All functions are specialized to the ARM7TDMI CPU. They should work with any later ARM CPU as well, but because of instruction pipeline differences they might have less than optimal performance.
These functions are intended to support Rust development on the GBA, but they're written in assembly so they should work with any language and any ARMv4T or later device.
Currently the code uses slightly alternate names from the "real" names so that it can be compiled into tests without clashing with the system version of each symbol.
The following functions are currently provided:
```rust extern "C" { pub fn libcmemcpy(d: *mut u8, s: *const u8, bytes: usize) -> *mut u8; pub fn aeabimemcpy(d: *mut u8, s: *const u8, bytes: usize); pub fn aeabimemcpy4(d: *mut u8, s: *const u8, bytes: usize); pub fn aeabimemcpy8(d: *mut u8, s: *const u8, bytes: usize); pub fn gbamemcpysram(d: *mut u8, s: *const u8, bytes: usize);
pub fn libcmemmove(d: *mut u8, s: *const u8, bytes: usize) -> *mut u8; pub fn aeabimemmove(d: *mut u8, s: *const u8, bytes: usize); pub fn aeabimemmove4(d: *mut u8, s: *const u8, bytes: usize); pub fn aeabimemmove8(d: *mut u8, s: *const u8, bytes: usize);
pub fn libcmemset(d: *mut u8, val: i32, bytes: usize) -> *mut u8; pub fn aeabimemset(d: *mut u8, bytes: usize, val: i32); pub fn aeabimemset4(d: *mut u8, bytes: usize, val: i32); pub fn aeabimemset8(d: *mut u8, bytes: usize, val: i32);
pub fn aeabimemclr(d: *mut u8, bytes: usize); pub fn aeabimemclr4(d: *mut u8, bytes: usize); pub fn aeabi_memclr8(d: *mut u8, bytes: usize);
pub fn aeabiuread4(address: *const u32) -> u32; pub fn aeabiuread8(address: *const u64) -> u64; pub fn aeabiuwrite4(value: u32, address: *mut u32) -> u32; pub fn aeabiuwrite8(value: u64, address: *mut u64) -> u64;
pub fn aeabiidiv(n: i32, d: i32) -> i32; pub fn aeabiuidiv(n: u32, d: u32) -> u32; pub fn aeabiidivmod(n: i32, d: i32) -> u64; pub fn aeabiuidivmod(n: u32, d: u32) -> u64; } ```
gba_memcpy_sram
function is not part of the AEABI, but lets you do a copy that is guaranteed
to access only one byte at a time. This makes it safe to use with the GBA's
SRAM.val as u8
too all bytes in the region. It
might seem silly to pass an i32
that's only used as a u8
, but memset
predates function prototypes in C, so that's how it works. Note that the
libc and aeabi versions have swapped argument order. This isn't a huge deal in
Rust, the compiler will catch when you mix it up.Functions with a 4 or 8 on the end require that input pointers be aligned to
that much. The bytes
value does not need to be an even multiple of the
alignment requirement.
All libc_
functions give the original destination pointer they were passed as
their return value. All aeabi_
functions return nothing at all (and this is
more efficient, so use them when possible).
Unfortunately, crates can't specify what link section they want a dependency's code to use. Since my main use for this assembly is to have it in a special section on the GBA so that the code is in RAM at runtime, I can't just publish it to crates.io and then use it as a normal dependency and have it go where I want. Or, I could, but then no one else could use it outside of that specific GBA context, which is also not great.
In the future this crate will be published as a proc-macro that accepts a
section name and emits a global_asm!
with the right contents. Until then, if
you want to use these functions just vendor the files into your project.
Testing of this crate is generally easiest using cross.
cross test --target arm-unknown-linux-gnueabi
Or, if you're running on an ARM device (eg: rpi with the 32-bit OS) then you can probably test natively I guess.
All the code here is released under CCO
.
OR (if you really want to use the standard Rust project licenses) Apache-2.0
OR
MIT
can also be used.