mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
change implementation of vblank to use new interrupt handler
also fixes all examples
This commit is contained in:
parent
7b05da4783
commit
f8fb6ba7b4
|
@ -14,7 +14,7 @@ struct Vector2D {
|
|||
pub fn main() -> ! {
|
||||
let mut gba = agb::Gba::new();
|
||||
let mut bitmap = gba.display.video.bitmap3();
|
||||
let vblank = gba.display.vblank.get();
|
||||
let vblank = agb::interrupt::VBlank::new();
|
||||
|
||||
let mut input = agb::input::ButtonController::new();
|
||||
let mut pos = Vector2D {
|
||||
|
@ -23,7 +23,7 @@ pub fn main() -> ! {
|
|||
};
|
||||
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
|
||||
input.update();
|
||||
pos.x += input.x_tri() as i32;
|
||||
|
|
|
@ -9,7 +9,7 @@ use agb::display;
|
|||
pub fn main() -> ! {
|
||||
let mut gba = agb::Gba::new();
|
||||
let mut bitmap = gba.display.video.bitmap4();
|
||||
let vblank = gba.display.vblank.get();
|
||||
let vblank = agb::interrupt::VBlank::new();
|
||||
|
||||
bitmap.set_palette_entry(1, 0x001F);
|
||||
bitmap.set_palette_entry(2, 0x03E0);
|
||||
|
@ -30,7 +30,7 @@ pub fn main() -> ! {
|
|||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
count += 1;
|
||||
if count % 6 == 0 {
|
||||
bitmap.flip_page();
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn main() -> ! {
|
|||
|
||||
let mut gba = agb::Gba::new();
|
||||
let mut gfx = gba.display.video.tiled0();
|
||||
let vblank = gba.display.vblank.get();
|
||||
let vblank = agb::interrupt::VBlank::new();
|
||||
let mut input = agb::input::ButtonController::new();
|
||||
|
||||
gfx.set_sprite_palette_raw(&CHICKEN_PALETTE);
|
||||
|
@ -90,7 +90,7 @@ pub fn main() -> ! {
|
|||
let terminal_velocity = (1 << 8) / 2;
|
||||
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
frame_count += 1;
|
||||
|
||||
input.update();
|
||||
|
|
|
@ -15,7 +15,7 @@ const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you
|
|||
pub fn main() -> ! {
|
||||
let mut gba = Gba::new();
|
||||
let mut input = ButtonController::new();
|
||||
let vblank_provider = gba.display.vblank.get();
|
||||
let vblank_provider = agb::interrupt::VBlank::new();
|
||||
|
||||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
@ -50,7 +50,7 @@ pub fn main() -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
vblank_provider.wait_for_VBlank();
|
||||
vblank_provider.wait_for_vblank();
|
||||
mixer.vblank();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,18 +13,18 @@ struct Vector2D {
|
|||
#[no_mangle]
|
||||
pub fn main() -> ! {
|
||||
let mut gba = agb::Gba::new();
|
||||
let mut vblank = gba.display.vblank.get();
|
||||
let mut vblank = agb::interrupt::VBlank::new();
|
||||
let mut input = agb::input::ButtonController::new();
|
||||
|
||||
loop {
|
||||
bitmap3_mode(&mut gba.display.video.bitmap3(), &mut vblank, &mut input);
|
||||
bitmap4_mode(&mut gba.display.video.bitmap4(), &mut vblank, &mut input);
|
||||
bitmap3_mode(&mut gba.display.video.bitmap3(), &vblank, &mut input);
|
||||
bitmap4_mode(&mut gba.display.video.bitmap4(), &vblank, &mut input);
|
||||
}
|
||||
}
|
||||
|
||||
fn bitmap3_mode(
|
||||
bitmap: &mut display::bitmap3::Bitmap3,
|
||||
vblank: &mut display::vblank::VBlank,
|
||||
vblank: &agb::interrupt::VBlank,
|
||||
input: &mut agb::input::ButtonController,
|
||||
) {
|
||||
let mut pos = Vector2D {
|
||||
|
@ -33,7 +33,7 @@ fn bitmap3_mode(
|
|||
};
|
||||
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
|
||||
input.update();
|
||||
if input.is_just_pressed(agb::input::Button::B) {
|
||||
|
@ -51,7 +51,7 @@ fn bitmap3_mode(
|
|||
|
||||
fn bitmap4_mode(
|
||||
bitmap: &mut display::bitmap4::Bitmap4,
|
||||
vblank: &mut display::vblank::VBlank,
|
||||
vblank: &agb::interrupt::VBlank,
|
||||
input: &mut agb::input::ButtonController,
|
||||
) {
|
||||
bitmap.set_palette_entry(1, 0x001F);
|
||||
|
@ -72,7 +72,7 @@ fn bitmap4_mode(
|
|||
|
||||
let mut count = 0;
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
|
||||
input.update();
|
||||
if input.is_just_pressed(agb::input::Button::B) {
|
||||
|
|
|
@ -6,11 +6,11 @@ extern crate agb;
|
|||
pub fn main() -> ! {
|
||||
let mut gba = agb::Gba::new();
|
||||
|
||||
let vblank = gba.display.vblank.get();
|
||||
let vblank = agb::interrupt::VBlank::new();
|
||||
|
||||
let mut count = 0;
|
||||
loop {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
|
||||
agb::println!("Hello, world, frame = {}", count);
|
||||
|
||||
|
|
62
agb/examples/wave.rs
Normal file
62
agb/examples/wave.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate agb;
|
||||
|
||||
use agb::{
|
||||
display::{example_logo, tiled0::Background},
|
||||
interrupt::{Interrupt, Mutex},
|
||||
number::FixedNum,
|
||||
};
|
||||
|
||||
struct BackCosines {
|
||||
cosines: [u16; 32],
|
||||
row: usize,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> ! {
|
||||
let mut gba = agb::Gba::new();
|
||||
let mut gfx = gba.display.video.tiled0();
|
||||
|
||||
gfx.set_background_palettes(example_logo::PALETTE_DATA);
|
||||
gfx.set_background_tilemap(0, example_logo::TILE_DATA);
|
||||
|
||||
let mut back = gfx.get_background().unwrap();
|
||||
|
||||
let mut entries: [u16; 30 * 20] = [0; 30 * 20];
|
||||
for tile_id in 0..(30 * 20) {
|
||||
let palette_entry = example_logo::PALETTE_ASSIGNMENT[tile_id as usize] as u16;
|
||||
entries[tile_id as usize] = tile_id | (palette_entry << 12);
|
||||
}
|
||||
|
||||
back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0);
|
||||
back.show();
|
||||
|
||||
let mut time = 0;
|
||||
let cosines = [0_u16; 32];
|
||||
|
||||
let back = Mutex::new(BackCosines { cosines, row: 0 });
|
||||
|
||||
agb::add_interrupt_handler!(Interrupt::HBlank, || {
|
||||
let mut backc = back.lock();
|
||||
let deflection = backc.cosines[backc.row % 32];
|
||||
unsafe { ((0x0400_0010) as *mut u16).write_volatile(deflection) }
|
||||
backc.row += 1;
|
||||
});
|
||||
|
||||
let vblank = agb::interrupt::VBlank::new();
|
||||
|
||||
loop {
|
||||
vblank.wait_for_vblank();
|
||||
let mut backc = back.lock();
|
||||
backc.row = 0;
|
||||
time += 1;
|
||||
for (r, a) in backc.cosines.iter_mut().enumerate() {
|
||||
let n: FixedNum<8> = (FixedNum::new(r as i32) / 32 + FixedNum::new(time) / 128).cos()
|
||||
* (256 * 4 - 1)
|
||||
/ 256;
|
||||
*a = (n.trunc() % (32 * 8)) as u16;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use crate::memory_mapped::MemoryMapped;
|
||||
use bitflags::bitflags;
|
||||
|
||||
use vblank::VBlankGiver;
|
||||
use video::Video;
|
||||
|
||||
use self::object::ObjectControl;
|
||||
|
@ -20,13 +19,11 @@ pub mod palette16;
|
|||
pub mod tile_data;
|
||||
/// Graphics mode 0. Four regular backgrounds.
|
||||
pub mod tiled0;
|
||||
/// Syscall for waiting for vblank.
|
||||
pub mod vblank;
|
||||
/// Giving out graphics mode.
|
||||
pub mod video;
|
||||
|
||||
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
|
||||
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
|
||||
pub(crate) const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
|
||||
const VCOUNT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0006) };
|
||||
|
||||
bitflags! {
|
||||
|
@ -65,7 +62,6 @@ enum DisplayMode {
|
|||
/// Manages distribution of display modes, obtained from the gba struct
|
||||
pub struct Display {
|
||||
pub video: Video,
|
||||
pub vblank: VBlankGiver,
|
||||
pub object: ObjectDistribution,
|
||||
}
|
||||
|
||||
|
@ -82,7 +78,6 @@ impl Display {
|
|||
pub(crate) const unsafe fn new() -> Self {
|
||||
Display {
|
||||
video: Video {},
|
||||
vblank: VBlankGiver {},
|
||||
object: ObjectDistribution {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,10 +118,10 @@ impl Background {
|
|||
}
|
||||
|
||||
fn set_x(&self, x: u16) {
|
||||
unsafe { *((0x0400_0010 + 4 * self.background as usize) as *mut u16) = x }
|
||||
unsafe { ((0x0400_0010 + 4 * self.background as usize) as *mut u16).write_volatile(x) }
|
||||
}
|
||||
fn set_y(&self, y: u16) {
|
||||
unsafe { *((0x0400_0012 + 4 * self.background as usize) as *mut u16) = y }
|
||||
unsafe { ((0x0400_0012 + 4 * self.background as usize) as *mut u16).write_volatile(y) }
|
||||
}
|
||||
|
||||
/// Forces the portion of the map in current view to be copied to the map
|
||||
|
@ -189,6 +189,11 @@ impl Background {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_offset(&mut self, position: Vector2D<u32>) {
|
||||
self.set_x(position.x as u16);
|
||||
self.set_y(position.y as u16);
|
||||
}
|
||||
|
||||
/// Sets the position of the map to be shown on screen. This automatically
|
||||
/// manages copying the correct portion to the map block and moving the map
|
||||
/// registers.
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use super::DISPLAY_STATUS;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct VBlankGiver {}
|
||||
|
||||
impl VBlankGiver {
|
||||
/// Gets a vblank handle where only one can be obtained at a time
|
||||
pub fn get(&mut self) -> VBlank {
|
||||
unsafe { VBlank::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Once obtained, this guarentees that interrupts are enabled and set up to
|
||||
/// allow for waiting for vblank
|
||||
pub struct VBlank {}
|
||||
|
||||
impl VBlank {
|
||||
unsafe fn new() -> Self {
|
||||
crate::interrupt::enable_interrupts();
|
||||
crate::interrupt::enable(crate::interrupt::Interrupt::VBlank);
|
||||
enable_VBlank_interrupt();
|
||||
VBlank {}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
/// Waits for VBlank using interrupts. This is the preferred method for
|
||||
/// waiting until the next frame.
|
||||
pub fn wait_for_VBlank(&self) {
|
||||
crate::syscall::wait_for_VBlank();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VBlank {
|
||||
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)]
|
||||
unsafe fn disable_VBlank_interrupt() {
|
||||
let status = DISPLAY_STATUS.get() & !(1 << 3);
|
||||
DISPLAY_STATUS.set(status);
|
||||
}
|
|
@ -5,9 +5,9 @@ use core::{
|
|||
pin::Pin,
|
||||
};
|
||||
|
||||
use crate::memory_mapped::MemoryMapped;
|
||||
use crate::{display::DISPLAY_STATUS, memory_mapped::MemoryMapped};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Interrupt {
|
||||
VBlank = 0,
|
||||
HBlank = 1,
|
||||
|
@ -28,21 +28,47 @@ pub enum Interrupt {
|
|||
const ENABLED_INTERRUPTS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x04000200) };
|
||||
const INTERRUPTS_ENABLED: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x04000208) };
|
||||
|
||||
pub(crate) fn enable(interrupt: Interrupt) {
|
||||
fn enable(interrupt: Interrupt) {
|
||||
let _interrupt_token = temporary_interrupt_disable();
|
||||
other_things_to_enable_interrupt(interrupt);
|
||||
let interrupt = interrupt as usize;
|
||||
let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16));
|
||||
ENABLED_INTERRUPTS.set(enabled);
|
||||
}
|
||||
|
||||
pub(crate) fn disable(interrupt: Interrupt) {
|
||||
fn disable(interrupt: Interrupt) {
|
||||
let _interrupt_token = temporary_interrupt_disable();
|
||||
other_things_to_disable_interrupt(interrupt);
|
||||
let interrupt = interrupt as usize;
|
||||
let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16));
|
||||
ENABLED_INTERRUPTS.set(enabled);
|
||||
}
|
||||
|
||||
pub(crate) struct Disable {}
|
||||
fn other_things_to_enable_interrupt(interrupt: Interrupt) {
|
||||
match interrupt {
|
||||
Interrupt::VBlank => {
|
||||
DISPLAY_STATUS.set_bits(1, 1, 3);
|
||||
}
|
||||
Interrupt::HBlank => {
|
||||
DISPLAY_STATUS.set_bits(1, 1, 4);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn other_things_to_disable_interrupt(interrupt: Interrupt) {
|
||||
match interrupt {
|
||||
Interrupt::VBlank => {
|
||||
DISPLAY_STATUS.set_bits(0, 1, 3);
|
||||
}
|
||||
Interrupt::HBlank => {
|
||||
DISPLAY_STATUS.set_bits(0, 1, 4);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
struct Disable {}
|
||||
|
||||
impl Drop for Disable {
|
||||
fn drop(&mut self) {
|
||||
|
@ -50,46 +76,66 @@ impl Drop for Disable {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn temporary_interrupt_disable() -> Disable {
|
||||
fn temporary_interrupt_disable() -> Disable {
|
||||
disable_interrupts();
|
||||
Disable {}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_interrupts() {
|
||||
fn enable_interrupts() {
|
||||
INTERRUPTS_ENABLED.set(1);
|
||||
}
|
||||
|
||||
pub(crate) fn disable_interrupts() {
|
||||
fn disable_interrupts() {
|
||||
INTERRUPTS_ENABLED.set(0);
|
||||
}
|
||||
|
||||
pub(crate) struct InterruptRoot {
|
||||
next: Cell<*const InterruptClosure>,
|
||||
count: Cell<i32>,
|
||||
interrupt: Interrupt,
|
||||
}
|
||||
|
||||
impl InterruptRoot {
|
||||
const fn new() -> Self {
|
||||
const fn new(interrupt: Interrupt) -> Self {
|
||||
InterruptRoot {
|
||||
next: Cell::new(core::ptr::null()),
|
||||
count: Cell::new(0),
|
||||
interrupt,
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(&self) {
|
||||
let new_count = self.count.get() - 1;
|
||||
if new_count == 0 {
|
||||
disable(self.interrupt);
|
||||
}
|
||||
self.count.set(new_count);
|
||||
}
|
||||
|
||||
fn add(&self) {
|
||||
let count = self.count.get();
|
||||
if count == 0 {
|
||||
enable(self.interrupt);
|
||||
}
|
||||
self.count.set(count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static mut INTERRUPT_TABLE: [InterruptRoot; 14] = [
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(),
|
||||
InterruptRoot::new(Interrupt::VBlank),
|
||||
InterruptRoot::new(Interrupt::HBlank),
|
||||
InterruptRoot::new(Interrupt::VCounter),
|
||||
InterruptRoot::new(Interrupt::Timer0),
|
||||
InterruptRoot::new(Interrupt::Timer1),
|
||||
InterruptRoot::new(Interrupt::Timer2),
|
||||
InterruptRoot::new(Interrupt::Timer3),
|
||||
InterruptRoot::new(Interrupt::Serial),
|
||||
InterruptRoot::new(Interrupt::Dma0),
|
||||
InterruptRoot::new(Interrupt::Dma1),
|
||||
InterruptRoot::new(Interrupt::Dma2),
|
||||
InterruptRoot::new(Interrupt::Dma3),
|
||||
InterruptRoot::new(Interrupt::Keypad),
|
||||
InterruptRoot::new(Interrupt::Gamepak),
|
||||
];
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -127,7 +173,9 @@ impl InterruptRoot {
|
|||
|
||||
impl Drop for InterruptClosure {
|
||||
fn drop(&mut self) {
|
||||
let mut c = unsafe { &*self.root }.next.get();
|
||||
let root = unsafe { &*self.root };
|
||||
root.reduce();
|
||||
let mut c = root.next.get();
|
||||
let own_pointer = self as *const _;
|
||||
if c == own_pointer {
|
||||
unsafe { &*self.root }.next.set(self.next.get());
|
||||
|
@ -174,6 +222,7 @@ pub fn get_interrupt_handle(
|
|||
|
||||
pub fn add_interrupt<'a>(interrupt: Pin<&'a InterruptClosureBounded<'a>>) {
|
||||
let root = unsafe { &*interrupt.c.root };
|
||||
root.add();
|
||||
let mut c = root.next.get();
|
||||
if c.is_null() {
|
||||
root.next.set((&interrupt.c) as *const _);
|
||||
|
@ -201,17 +250,17 @@ macro_rules! add_interrupt_handler {
|
|||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_vblank_interrupt_handler(gba: &mut crate::Gba) {
|
||||
fn test_vblank_interrupt_handler(_gba: &mut crate::Gba) {
|
||||
{
|
||||
let counter = Mutex::new(0);
|
||||
let counter_2 = Mutex::new(0);
|
||||
add_interrupt_handler!(Interrupt::VBlank, || *counter.lock() += 1);
|
||||
add_interrupt_handler!(Interrupt::VBlank, || *counter_2.lock() += 1);
|
||||
|
||||
let vblank = gba.display.vblank.get();
|
||||
let vblank = VBlank::new();
|
||||
|
||||
while *counter.lock() < 100 || *counter_2.lock() < 100 {
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,3 +348,22 @@ impl<'a, T> DerefMut for MutexRef<'a, T> {
|
|||
unsafe { &mut *self.internal.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct VBlank {}
|
||||
|
||||
impl VBlank {
|
||||
pub fn new() -> Self {
|
||||
interrupt_to_root(Interrupt::VBlank).add();
|
||||
VBlank {}
|
||||
}
|
||||
pub fn wait_for_vblank(&self) {
|
||||
crate::syscall::wait_for_VBlank();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VBlank {
|
||||
fn drop(&mut self) {
|
||||
interrupt_to_root(Interrupt::VBlank).reduce();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,14 +163,14 @@ mod test {
|
|||
}
|
||||
|
||||
#[test_case]
|
||||
fn wait_30_frames(gba: &mut Gba) {
|
||||
let vblank = gba.display.vblank.get();
|
||||
fn wait_30_frames(_gba: &mut Gba) {
|
||||
let vblank = crate::interrupt::VBlank::new();
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
if counter > 30 {
|
||||
break;
|
||||
}
|
||||
vblank.wait_for_VBlank();
|
||||
vblank.wait_for_vblank();
|
||||
counter += 1
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue