diff --git a/Cargo.toml b/Cargo.toml index 6e0ae5a..4de83a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ publish = false [dependencies] gba-proc-macro = "0.2.1" +[profile.dev] +panic = "abort" + [profile.release] lto = true panic = "abort" diff --git a/examples/key_demo.rs b/examples/key_demo.rs index d63e68d..efe3d61 100644 --- a/examples/key_demo.rs +++ b/examples/key_demo.rs @@ -34,6 +34,7 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize { let key = ((this_frame_key_raw >> i) & 1) > 0; mode3_draw_pixel(15 - i, 0, if key { green } else { red }); mode3_draw_pixel(15 - i, 1, if key { green } else { red }); + mode3_draw_pixel(50 - i, 10 / i, if key { green } else { red }); } wait_until_vdraw(); diff --git a/src/intrinsics.rs b/src/intrinsics.rs new file mode 100644 index 0000000..dc92f3e --- /dev/null +++ b/src/intrinsics.rs @@ -0,0 +1,71 @@ +//! Intrinsics that LLVM looks for when compiling. + +/// Signed division +#[no_mangle] +#[inline] +pub extern "aapcs" fn __aeabi_idiv(n: i32, d: i32) -> i32 { + assert!(d != 0); + let div_out: i32; + let _mod_out: i32; + unsafe { + asm!(/* assembly template */ "swi 0x06" + :/* output operands */ "={r0}"(div_out), "={r1}"(_mod_out) + :/* input operands */ "{r0}"(n), "{r1}"(d) + :/* clobbers */ "r3" + :/* options */ + ); + } + div_out +} + +/// Signed division alias for glibc reasons +#[no_mangle] +#[inline] +pub extern "aapcs" fn __divsi3(n: i32, d: i32) -> i32 { + // Note the different naming scheme. + __aeabi_idiv(n, d) +} + +/// Unsigned division gets cast into signed values, divided, and cast back +#[no_mangle] +#[inline] +pub extern "aapcs" fn __aeabi_uidiv(n: u32, d: u32) -> u32 { + __aeabi_idiv(n as i32, d as i32) as u32 +} + +/// Unsigned division alias for glibc reasons +#[no_mangle] +#[inline] +pub extern "aapcs" fn __udivsi3(n: u32, d: u32) -> u32 { + // Note the different naming scheme. + __aeabi_uidiv(n, d) +} + +/// Count leading zeroes, required in debug mode for unknown reasons +#[no_mangle] +#[inline] +pub extern "aapcs" fn __clzsi2(x: i32) -> i32 { + let mut y = -(x >> 16); // If left half of x is 0, + let mut m = (y >> 16) & 16; // set n = 16. If left half + let mut n = 16 - m; // is nonzero, set n = 0 and + let mut x = x >> m; // shift x right 16. + // Now x is of the form 0000xxxx. + y = x - 0x100; // If positions 8-15 are 0, + m = (y >> 16) & 8; // add 8 to n and shift x left 8. + n = n + m; + x = x << m; + + y = x - 0x1000; // If positions 12-15 are 0, + m = (y >> 16) & 4; // add 4 to n and shift x left 4. + n = n + m; + x = x << m; + + y = x - 0x4000; // If positions 14-15 are 0, + m = (y >> 16) & 2; // add 2 to n and shift x left 2. + n = n + m; + x = x << m; + + y = x >> 14; // Set y = 0, 1, 2, or 3. + m = y & !(y >> 1); // Set m = 0, 1, 2, or 2 resp. + n + 2 - m +} diff --git a/src/io_registers.rs b/src/io_registers.rs index 120aae7..3682f64 100644 --- a/src/io_registers.rs +++ b/src/io_registers.rs @@ -116,6 +116,18 @@ pub fn vcount() -> u16 { unsafe { VCOUNT.read() } } +/// Performs a busy loop until VBlank starts. +pub fn wait_until_vblank() { + // TODO: make this the better version with BIOS and interrupts and such. + while vcount() < SCREEN_HEIGHT as u16 {} +} + +/// Performs a busy loop until VDraw starts. +pub fn wait_until_vdraw() { + // TODO: make this the better version with BIOS and interrupts and such. + while vcount() >= SCREEN_HEIGHT as u16 {} +} + /// BG0 Control pub const BG0CNT: VolatilePtr = VolatilePtr(0x4000008 as *mut u16); diff --git a/src/lib.rs b/src/lib.rs index 7c74928..ab35f5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(test), no_std)] +#![feature(asm)] #![warn(missing_docs)] //! This crate helps you write GBA ROMs. @@ -26,11 +27,41 @@ pub mod core_extras; pub(crate) use crate::core_extras::*; pub mod io_registers; -pub(crate) use crate::io_registers::*; pub mod video_ram; +pub(crate) use crate::video_ram::*; + +pub mod intrinsics; /// Combines the Red, Blue, and Green provided into a single color value. pub const fn rgb16(red: u16, green: u16, blue: u16) -> u16 { blue << 10 | green << 5 | red } + +/// BIOS Call: Div (GBA SWI 0x06). +/// +/// Gives the DIV and MOD output of `numerator / denominator`. +/// +/// # Panics +/// +/// If `denominator` is 0. +#[inline] +pub fn div_mod(numerator: i32, denominator: i32) -> (i32, i32) { + // The BIOS includes several System Call Functions which can be accessed by + // SWI instructions. Incoming parameters are usually passed through registers + // R0,R1,R2,R3. Outgoing registers R0,R1,R3 are typically containing either + // garbage, or return value(s). All other registers (R2,R4-R14) are kept + // unchanged. --GBATEK + assert!(denominator != 0); + let div_out: i32; + let mod_out: i32; + unsafe { + asm!(/* assembly template */ "swi 0x06" + :/* output operands */ "={r0}"(div_out), "={r1}"(mod_out) + :/* input operands */ "{r0}"(numerator), "{r1}"(denominator) + :/* clobbers */ "r3" + :/* options */ + ); + } + (div_out, mod_out) +} diff --git a/src/video_ram.rs b/src/video_ram.rs index de9ac04..c4d1601 100644 --- a/src/video_ram.rs +++ b/src/video_ram.rs @@ -28,18 +28,6 @@ pub const SCREEN_HEIGHT: isize = 160; /// value as just being a `usize`. pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000; -/// Performs a busy loop until VBlank starts. -pub fn wait_until_vblank() { - // TODO: make this the better version with BIOS and interrupts and such. - while vcount() < SCREEN_HEIGHT as u16 {} -} - -/// Performs a busy loop until VDraw starts. -pub fn wait_until_vdraw() { - // TODO: make this the better version with BIOS and interrupts and such. - while vcount() >= SCREEN_HEIGHT as u16 {} -} - /// Draws a pixel to the screen while in Display Mode 3, with bounds checks. /// /// # Panics