diff --git a/examples/paddle_ball.rs b/examples/paddle_ball.rs index 2a6ff83..6e942a5 100644 --- a/examples/paddle_ball.rs +++ b/examples/paddle_ball.rs @@ -1,24 +1,15 @@ -#![allow(unused_imports)] -/* - * Made by Evan Goemer - * Discord: @evangoemer - */ #![no_std] #![no_main] -use core::ptr::addr_of; +//! Made by Evan Goemer, Discord: @evangoemer use gba::{ asm_runtime::USER_IRQ_HANDLER, bios::VBlankIntrWait, - dma::{DmaControl, DmaSrcAddr}, gba_cell::GbaCell, mem::bulk_memory_set, - mmio::{ - DISPCNT, DISPSTAT, DMA3_CONTROL, DMA3_DESTINATION, DMA3_SOURCE, - DMA3_TRANSFER_COUNT, IE, IME, KEYINPUT, MODE3_VRAM, OBJ_PALRAM, - }, - video::{Color, DisplayControl, DisplayStatus}, + mmio::{DISPCNT, DISPSTAT, IE, IME, KEYINPUT, MODE3_VRAM}, + video::{Color, DisplayControl, DisplayStatus, Mode3}, IrqBits, KeyInput, }; @@ -151,29 +142,7 @@ fn main() -> ! { } extern "C" fn draw_sprites(_bits: IrqBits) { - /* - unsafe { - let color = OBJ_PALRAM.index(0).read(); - let c: u32 = color.0 as u32 | (color.0 as u32) << 16; - DMA3_SOURCE.write(addr_of!(c).cast()); - DMA3_DESTINATION.write(MODE3_VRAM.as_usize() as _); - DMA3_TRANSFER_COUNT.write(SCREEN_WIDTH * SCREEN_HEIGHT / 2); - DMA3_CONTROL.write( - DmaControl::new() - .with_src_addr(DmaSrcAddr::Fixed) - .with_u32_transfer(true) - .with_enabled(true), - ); - } - // */ - unsafe { - let dest = MODE3_VRAM.as_usize() as *mut u32; - let byte_count = SCREEN_WIDTH as usize - * SCREEN_HEIGHT as usize - * core::mem::size_of::(); - let r2 = 0; - bulk_memory_set(dest, byte_count, r2); - } + Mode3.clear_to(Color::BLACK); draw_rect( SPRITE_POSITIONS[0].read(), @@ -198,6 +167,8 @@ extern "C" fn draw_sprites(_bits: IrqBits) { ); } +// we out-line this because otherwise `draw_sprites` overloads the stack when it +// holds 3 copies of this function. #[inline(never)] fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { for i in 0..width { diff --git a/src/video.rs b/src/video.rs index fe3f556..fefec61 100644 --- a/src/video.rs +++ b/src/video.rs @@ -1,4 +1,4 @@ -//! +//! Module for screen-related types and functions. use bitfrob::{u16_get_bit, u16_with_bit, u16_with_value}; @@ -326,3 +326,59 @@ pub struct Tile4bpp(pub [u32; 8]); #[derive(Clone, Copy, Default)] #[repr(transparent)] pub struct Tile8bpp(pub [u32; 16]); + +/// A zero-sized type that gives a namespace for Mode 3 related things. +#[derive(Clone, Copy)] +pub struct Mode3; +impl Mode3 { + /// Width, in pixels, of the Mode 3 bitmap. + pub const WIDTH: usize = 240; + + /// Height, in pixels, of the Mode 3 bitmap. + pub const HEIGHT: usize = 160; + + /// The size, in bytes, of the Mode 3 bitmap. + pub const BYTES: usize = + Self::WIDTH * Self::HEIGHT * core::mem::size_of::(); + + /// Clears the entire bitmap to a color of your choosing. + #[cfg_attr(feature = "on_gba", instruction_set(arm::a32))] + #[cfg_attr(feature = "on_gba", link_section = ".iwram.mode3.clear_to")] + pub fn clear_to(self, color: Color) { + on_gba_or_unimplemented!(unsafe { + let x: u32 = color.0 as u32 | ((color.0 as u32) << 16); + // now we spam out that `u32`, 10 stm per loop, 8 times per stm. + core::arch::asm!( + "1:", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "stm {ptr}!, {{r0-r5,r7-r8}}", + "subs {count}, {count}, #1", + "bne 1b", + + // The assembler will give us a warning (that we can't easily disable) + // if the reg_list for `stm` doesn't give the registers in order from + // low to high, so we just manually pick registers. The count register + // and the pointer register can be anything else. + in("r0") x, + in("r1") x, + in("r2") x, + in("r3") x, + in("r4") x, + in("r5") x, + in("r7") x, + in("r8") x, + count = inout(reg) 240 => _, + ptr = inout(reg) crate::mmio::MODE3_VRAM.as_usize() => _, + options(nostack), + ) + }); + } +}