gba/src/lib.rs

180 lines
5.3 KiB
Rust
Raw Normal View History

#![no_std]
2021-05-02 10:38:52 +10:00
#![feature(asm, global_asm, pub_macro_rules, isa_attribute)]
//! This crate helps you write GBA ROMs.
//!
//! ## Safety
//!
//! This crate takes *minimal* precautions to avoid GBA specific code from being
//! run on a standard desktop by accident by using `#[cfg(target_arch = "arm")]`
//! in appropriate places. However, there are obviously many other ARM devices
//! in the world. If you actually run the GBA specific code on something that
//! isn't a GBA, then that's your fault.
//!
//! ## Docs.rs
//!
//! The docs on docs.rs are generated for the `thumbv6m-none-eabi` target
//! because the docs.rs docker image isn't currently able to use the
//! `-Zbuild-std=core` ability of cargo. Instead, we have it just build using a
//! "close enough" Tier 2 target.
//!
//! When building your actual GBA games you should of course use the
//! `thumbv4t-none-eabi` target.
2018-12-30 14:17:48 +11:00
pub mod prelude {
pub use crate::mmio_types::*;
2018-12-30 14:17:48 +11:00
2021-05-02 10:38:52 +10:00
#[cfg(target_arch = "arm")]
pub use crate::bios::*;
#[cfg(target_arch = "arm")]
pub use crate::debugging::warning;
#[cfg(target_arch = "arm")]
pub use crate::debugging::*;
#[cfg(target_arch = "arm")]
pub use crate::mmio_addresses::*;
#[cfg(target_arch = "arm")]
2021-05-02 10:38:52 +10:00
pub use crate::save::*;
#[cfg(target_arch = "arm")]
pub use crate::sync::*;
}
2018-12-30 14:17:48 +11:00
pub mod mmio_types;
2018-12-30 14:17:48 +11:00
#[cfg(target_arch = "arm")]
pub mod mmio_addresses;
2018-12-30 14:17:48 +11:00
#[cfg(target_arch = "arm")]
pub mod bios;
pub mod art;
2018-12-30 14:17:48 +11:00
#[cfg(target_arch = "arm")]
pub mod sync;
2018-12-30 14:17:48 +11:00
#[cfg(target_arch = "arm")]
pub mod save;
2018-12-30 14:17:48 +11:00
#[cfg(target_arch = "arm")]
2021-05-02 10:38:52 +10:00
pub mod debugging;
/*
2019-01-13 07:42:18 +11:00
extern "C" {
/// This marks the end of the `.data` and `.bss` sections in IWRAM.
///
/// Memory in IWRAM _before_ this location is not free to use, you'll trash
/// your globals and stuff. Memory here or after is freely available for use
/// (careful that you don't run into your own stack of course).
///
/// The actual value is unimportant, you just want to use the _address of_
/// this location as the start of your IWRAM usage.
2019-02-10 08:35:48 +11:00
pub static __bss_end: u8;
2019-01-13 07:42:18 +11:00
}
TODO: math module for math functions you probably want on the GBA
/// Performs unsigned divide and remainder, gives None if dividing by 0.
pub fn divrem_u32(numer: u32, denom: u32) -> Option<(u32, u32)> {
// TODO: const this? Requires const if
if denom == 0 {
None
} else {
Some(unsafe { divrem_u32_unchecked(numer, denom) })
}
}
/// Performs divide and remainder, no check for 0 division.
///
/// # Safety
///
/// If you call this with a denominator of 0 the result is implementation
/// defined (not literal UB) including but not limited to: an infinite loop,
/// panic on overflow, or incorrect output.
pub unsafe fn divrem_u32_unchecked(numer: u32, denom: u32) -> (u32, u32) {
// TODO: const this? Requires const if
if (numer >> 5) < denom {
divrem_u32_simple(numer, denom)
} else {
divrem_u32_non_restoring(numer, denom)
}
}
/// The simplest form of division. If N is too much larger than D this will be
/// extremely slow. If N is close enough to D then it will likely be faster than
/// the non_restoring form.
fn divrem_u32_simple(mut numer: u32, denom: u32) -> (u32, u32) {
// TODO: const this? Requires const if
let mut quot = 0;
while numer >= denom {
numer -= denom;
quot += 1;
}
(quot, numer)
}
/// Takes a fixed quantity of time based on the bit width of the number (in this
/// case 32).
fn divrem_u32_non_restoring(numer: u32, denom: u32) -> (u32, u32) {
// TODO: const this? Requires const if
let mut r: i64 = numer as i64;
let d: i64 = (denom as i64) << 32;
let mut q: u32 = 0;
let mut i = 1 << 31;
while i > 0 {
if r >= 0 {
q |= i;
r = 2 * r - d;
} else {
r = 2 * r + d;
}
i >>= 1;
}
q -= !q;
if r < 0 {
q -= 1;
r += d;
}
r >>= 32;
// TODO: remove this once we've done more checks here.
debug_assert!(r >= 0);
debug_assert!(r <= core::u32::MAX as i64);
(q, r as u32)
}
/// Performs signed divide and remainder, gives None if dividing by 0 or
/// computing `MIN/-1`
pub fn divrem_i32(numer: i32, denom: i32) -> Option<(i32, i32)> {
if denom == 0 || (numer == core::i32::MIN && denom == -1) {
None
} else {
Some(unsafe { divrem_i32_unchecked(numer, denom) })
}
}
/// Performs signed divide and remainder, no check for 0 division or `MIN/-1`.
///
/// # Safety
///
/// * If you call this with a denominator of 0 the result is implementation
/// defined (not literal UB) including but not limited to: an infinite loop,
/// panic on overflow, or incorrect output.
/// * If you call this with `MIN/-1` you'll get a panic in debug or just `MIN`
/// in release (which is incorrect), because of how twos-compliment works.
pub unsafe fn divrem_i32_unchecked(numer: i32, denom: i32) -> (i32, i32) {
// TODO: const this? Requires const if
let unsigned_numer = numer.abs() as u32;
let unsigned_denom = denom.abs() as u32;
let opposite_sign = (numer ^ denom) < 0;
let (udiv, urem) = if (numer >> 5) < denom {
divrem_u32_simple(unsigned_numer, unsigned_denom)
} else {
divrem_u32_non_restoring(unsigned_numer, unsigned_denom)
};
match (opposite_sign, numer < 0) {
(true, true) => (-(udiv as i32), -(urem as i32)),
(true, false) => (-(udiv as i32), urem as i32),
(false, true) => (udiv as i32, -(urem as i32)),
(false, false) => (udiv as i32, urem as i32),
}
}
*/