allow only one vblank handler

This commit is contained in:
Corwin Kuiper 2021-03-07 00:47:39 +00:00
parent 3978e64cff
commit 9527d32521
3 changed files with 57 additions and 18 deletions

View file

@ -14,6 +14,7 @@ struct Vector2D {
fn main(_argc: isize, _argv: *const *const u8) -> isize { fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new(); let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3(); let bitmap = gba.display.bitmap3();
let vblank = gba.display.get_vblank();
let mut input = gba::input::ButtonController::new(); let mut input = gba::input::ButtonController::new();
let mut pos = Vector2D { let mut pos = Vector2D {
@ -21,12 +22,8 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
y: display::HEIGHT / 2, y: display::HEIGHT / 2,
}; };
gba::interrupt::enable(gba::interrupt::Interrupt::VBlank);
gba::interrupt::enable_interrupts();
gba::display::enable_VBlank_interrupt();
loop { loop {
gba::display::wait_for_VBlank(); vblank.wait_for_VBlank();
input.update(); input.update();
pos.x += input.x_tri() as i32; pos.x += input.x_tri() as i32;

View file

@ -9,6 +9,7 @@ use gba::display;
fn main(_argc: isize, _argv: *const *const u8) -> isize { fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new(); let gba = gba::Gba::new();
let bitmap = gba.display.bitmap4(); let bitmap = gba.display.bitmap4();
let vblank = gba.display.get_vblank();
bitmap.set_palette_entry(1, 0x001F); bitmap.set_palette_entry(1, 0x001F);
bitmap.set_palette_entry(2, 0x03E0); bitmap.set_palette_entry(2, 0x03E0);
@ -17,23 +18,19 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
display::WIDTH / 2, display::WIDTH / 2,
display::HEIGHT / 2, display::HEIGHT / 2,
1, 1,
display::Page::Front, display::bitmap4::Page::Front,
); );
bitmap.draw_point_page( bitmap.draw_point_page(
display::WIDTH / 2 + 5, display::WIDTH / 2 + 5,
display::HEIGHT / 2, display::HEIGHT / 2,
2, 2,
display::Page::Back, display::bitmap4::Page::Back,
); );
gba::interrupt::enable(gba::interrupt::Interrupt::VBlank);
gba::interrupt::enable_interrupts();
gba::display::enable_VBlank_interrupt();
let mut count = 0; let mut count = 0;
loop { loop {
gba::display::wait_for_VBlank(); vblank.wait_for_VBlank();
count += 1; count += 1;
if count % 6 == 0 { if count % 6 == 0 {
bitmap.flip_page(); bitmap.flip_page();

View file

@ -1,11 +1,14 @@
use crate::{memory_mapped::MemoryMapped, single::Single}; use crate::{
memory_mapped::MemoryMapped,
single::{Single, SingleToken},
};
use bitflags::bitflags; use bitflags::bitflags;
use bitmap3::Bitmap3; use bitmap3::Bitmap3;
use bitmap4::Bitmap4; use bitmap4::Bitmap4;
mod bitmap3; pub mod bitmap3;
mod bitmap4; pub mod bitmap4;
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) }; const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) }; const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
@ -42,12 +45,14 @@ pub enum DisplayMode {
pub struct Display { pub struct Display {
in_mode: Single, in_mode: Single,
vblank: Single,
} }
impl Display { impl Display {
pub(crate) const unsafe fn new() -> Self { pub(crate) const unsafe fn new() -> Self {
Display { Display {
in_mode: Single::new(), in_mode: Single::new(),
vblank: Single::new(),
} }
} }
@ -65,6 +70,15 @@ impl Display {
.expect("Cannot create new mode as mode already taken"), .expect("Cannot create new mode as mode already taken"),
) )
} }
pub fn get_vblank(&self) -> VBlank {
unsafe {
VBlank::new(
self.vblank
.take()
.expect("Cannot create another vblank handler"),
)
}
}
} }
fn set_graphics_mode(mode: DisplayMode) { fn set_graphics_mode(mode: DisplayMode) {
@ -85,18 +99,49 @@ pub fn set_graphics_settings(settings: GraphicsSettings) {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
/// Waits until vblank using a busy wait loop, this should almost never be used.
/// I only say almost because whilst I don't believe there to be a reason to use
/// this I can't rule it out.
pub fn busy_wait_for_VBlank() { pub fn busy_wait_for_VBlank() {
while VCOUNT.get() >= 160 {} while VCOUNT.get() >= 160 {}
while VCOUNT.get() < 160 {} while VCOUNT.get() < 160 {}
} }
pub struct VBlank<'a> {
_got: SingleToken<'a>,
}
impl<'a> VBlank<'a> {
unsafe fn new(a: SingleToken<'a>) -> Self {
crate::interrupt::enable_interrupts();
crate::interrupt::enable(crate::interrupt::Interrupt::VBlank);
enable_VBlank_interrupt();
VBlank { _got: a }
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn enable_VBlank_interrupt() { pub fn wait_for_VBlank(&self) {
crate::syscall::wait_for_VBlank();
}
}
impl<'a> Drop for VBlank<'a> {
fn drop(&mut self) {
unsafe {
disable_VBlank_interrupt();
crate::interrupt::disable(crate::interrupt::Interrupt::VBlank);
}
}
}
#[allow(non_snake_case)]
unsafe fn enable_VBlank_interrupt() {
let status = DISPLAY_STATUS.get() | (1 << 3); let status = DISPLAY_STATUS.get() | (1 << 3);
DISPLAY_STATUS.set(status); DISPLAY_STATUS.set(status);
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn wait_for_VBlank() { unsafe fn disable_VBlank_interrupt() {
crate::syscall::wait_for_VBlank(); let status = DISPLAY_STATUS.get() & !(1 << 3);
DISPLAY_STATUS.set(status);
} }