partial intrinsic work

This commit is contained in:
Lokathor 2018-11-15 01:23:04 -07:00
parent 6cb50aa2eb
commit 8aa1bb0065
6 changed files with 119 additions and 13 deletions

View file

@ -14,6 +14,9 @@ publish = false
[dependencies]
gba-proc-macro = "0.2.1"
[profile.dev]
panic = "abort"
[profile.release]
lto = true
panic = "abort"

View file

@ -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();

71
src/intrinsics.rs Normal file
View file

@ -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
}

View file

@ -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<u16> = VolatilePtr(0x4000008 as *mut u16);

View file

@ -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)
}

View file

@ -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