mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 10:51: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)]
|
||||
|
||||
//! 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;
|
||||
|
||||
/// `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.
|
||||
///
|
||||
/// Waits for *any* of the interrupt types set in `target_irqs` to occur. Your
|
||||
/// interrupt handler (if any) will be run before this function returns.
|
||||
/// Your 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
|
||||
/// 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]
|
||||
#[instruction_set(arm::t32)]
|
||||
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