mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 19:01:30 +11:00
BitUnPack
This commit is contained in:
parent
174d196d71
commit
da46fe5df1
80
src/bios.rs
80
src/bios.rs
|
@ -1,13 +1,30 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
//! The GBA's BIOS provides limited built-in utility functions.
|
||||||
|
//!
|
||||||
|
//! BIOS functions are accessed with an `swi` instruction to perform a software
|
||||||
|
//! interrupt. This means that there's a *significant* overhead for a BIOS call
|
||||||
|
//! (tens of cycles) compared to a normal function call (3 cycles, or even none
|
||||||
|
//! of the function ends up inlined). Despite this higher cost, some bios
|
||||||
|
//! functions are useful enough to justify the overhead.
|
||||||
|
|
||||||
use crate::interrupts::IrqBits;
|
use crate::interrupts::IrqBits;
|
||||||
|
|
||||||
/// `0x04` Waits for a specific interrupt type(s) to happen.
|
// Note(Lokathor): All `swi` calls will preserve the flags. You should generally
|
||||||
|
// not use any other inline-asm options with `swi` calls.
|
||||||
|
|
||||||
|
/// `0x04`: Waits for a specific interrupt type(s) to happen.
|
||||||
|
///
|
||||||
|
/// Pauses the CPU until any of the interrupt types set in `target_irqs` to
|
||||||
|
/// occur. This can create a significant savings of the battery while you're
|
||||||
|
/// waiting, so use this function when possible.
|
||||||
///
|
///
|
||||||
/// **Important:** This function forces [`IME`](crate::mmio::IME) on.
|
/// **Important:** This function forces [`IME`](crate::mmio::IME) on.
|
||||||
///
|
///
|
||||||
/// Waits for *any* of the interrupt types set in `target_irqs` to occur. Your
|
/// Your interrupt handler (if any) will be run before this function returns.
|
||||||
/// interrupt handler (if any) will be run before this function returns.
|
///
|
||||||
|
/// If none of the interrupts specified in `target_irqs` are properly configured
|
||||||
|
/// to fire then this function will loop forever without returning.
|
||||||
///
|
///
|
||||||
/// This function uses a special BIOS variable to track what interrupts have
|
/// This function uses a special BIOS variable to track what interrupts have
|
||||||
/// occured recently.
|
/// occured recently.
|
||||||
|
@ -30,7 +47,7 @@ pub fn IntrWait(ignore_existing: bool, target_irqs: IrqBits) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `0x05` Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait)
|
/// `0x05`: Builtin shorthand for [`IntrWait(true, IrqBits::VBLANK)`](IntrWait)
|
||||||
#[inline]
|
#[inline]
|
||||||
#[instruction_set(arm::t32)]
|
#[instruction_set(arm::t32)]
|
||||||
pub fn VBlankIntrWait() {
|
pub fn VBlankIntrWait() {
|
||||||
|
@ -44,3 +61,58 @@ pub fn VBlankIntrWait() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to provide info to a call of the [`BitUnPack`] function.
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BitUnpackInfo {
|
||||||
|
/// Number of bytes in the source buffer
|
||||||
|
pub src_byte_len: u16,
|
||||||
|
/// Bits per source element: 1, 2, 4, or 8.
|
||||||
|
pub src_elem_width: u8,
|
||||||
|
/// Bits per destination element: 1, 2, 4, 8, 16, or 32.
|
||||||
|
pub dest_elem_width: u8,
|
||||||
|
/// Bits `0..=30` are the offset value added to all non-zero elements.
|
||||||
|
///
|
||||||
|
/// If bit `31` is set then offset value is *also* added to zero elements.
|
||||||
|
pub offset_and_touch_zero: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `0x10`: Copy data from `src` to `dest` while increasing the bit depth of the
|
||||||
|
/// elements copied.
|
||||||
|
///
|
||||||
|
/// * This reads one byte at a time from `src`. Each source byte holds 1 or more
|
||||||
|
/// source elements, depending on the source bit depth you specify. Elements
|
||||||
|
/// within a byte are packed from low bit to high bit.
|
||||||
|
/// * Each non-zero source element has the offset added to it. If the source
|
||||||
|
/// element is zero and the "touch zero" flag is set, then that source element
|
||||||
|
/// will also have the offset added to it. This creates a destination element.
|
||||||
|
/// * Destination elements are collected into the output `u32` buffer one at a
|
||||||
|
/// time, from low bit to high bit. If a source element plus the offset
|
||||||
|
/// produces a value larger than the destination element bit size this will
|
||||||
|
/// corrupt any following destination elements within the buffer. When the
|
||||||
|
/// buffer has 32 bits held then it's written to the destination pointer.
|
||||||
|
/// * When the source byte read has no more source elements remaining the source
|
||||||
|
/// pointer will advance and `src_byte_len` will go down by 1. When
|
||||||
|
/// `src_byte_len` goes to 0 the function's main loop will break and return.
|
||||||
|
/// If there was partial output in the `u32` buffer when the function's
|
||||||
|
/// primary loop ends this data will be lost.
|
||||||
|
///
|
||||||
|
/// ## Safety
|
||||||
|
/// * The `info` provided must correctly describe the data.
|
||||||
|
/// * `src` must be readable for the number of **bytes** specified
|
||||||
|
/// * `dest` must be writable for the number of **words** that the source
|
||||||
|
/// buffer, source depth, and destination depth will total up to.
|
||||||
|
/// * `dest` must be 4 byte aligned.
|
||||||
|
#[inline]
|
||||||
|
#[instruction_set(arm::t32)]
|
||||||
|
pub unsafe fn BitUnPack(src: *mut u8, dest: *mut u32, info: &BitUnpackInfo) {
|
||||||
|
core::arch::asm! {
|
||||||
|
"swi #0x05",
|
||||||
|
inout("r0") src => _,
|
||||||
|
inout("r1") dest => _,
|
||||||
|
inout("r2") info => _,
|
||||||
|
out("r3") _,
|
||||||
|
options(preserves_flags),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue