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 {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
let vblank = gba.display.get_vblank();
let mut input = gba::input::ButtonController::new();
let mut pos = Vector2D {
@ -21,12 +22,8 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
y: display::HEIGHT / 2,
};
gba::interrupt::enable(gba::interrupt::Interrupt::VBlank);
gba::interrupt::enable_interrupts();
gba::display::enable_VBlank_interrupt();
loop {
gba::display::wait_for_VBlank();
vblank.wait_for_VBlank();
input.update();
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 {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap4();
let vblank = gba.display.get_vblank();
bitmap.set_palette_entry(1, 0x001F);
bitmap.set_palette_entry(2, 0x03E0);
@ -17,23 +18,19 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
display::WIDTH / 2,
display::HEIGHT / 2,
1,
display::Page::Front,
display::bitmap4::Page::Front,
);
bitmap.draw_point_page(
display::WIDTH / 2 + 5,
display::HEIGHT / 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;
loop {
gba::display::wait_for_VBlank();
vblank.wait_for_VBlank();
count += 1;
if count % 6 == 0 {
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 bitmap3::Bitmap3;
use bitmap4::Bitmap4;
mod bitmap3;
mod bitmap4;
pub mod bitmap3;
pub mod bitmap4;
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
@ -42,12 +45,14 @@ pub enum DisplayMode {
pub struct Display {
in_mode: Single,
vblank: Single,
}
impl Display {
pub(crate) const unsafe fn new() -> Self {
Display {
in_mode: Single::new(),
vblank: Single::new(),
}
}
@ -65,6 +70,15 @@ impl Display {
.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) {
@ -85,18 +99,49 @@ pub fn set_graphics_settings(settings: GraphicsSettings) {
}
#[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() {
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)]
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);
DISPLAY_STATUS.set(status);
}
#[allow(non_snake_case)]
pub fn wait_for_VBlank() {
crate::syscall::wait_for_VBlank();
unsafe fn disable_VBlank_interrupt() {
let status = DISPLAY_STATUS.get() & !(1 << 3);
DISPLAY_STATUS.set(status);
}