This commit is contained in:
Lokathor 2021-04-11 17:41:08 -06:00
parent 0d21b4c899
commit e595270bfe
3 changed files with 137 additions and 21 deletions

View file

@ -2,7 +2,7 @@
#![no_main] #![no_main]
#![feature(isa_attribute)] #![feature(isa_attribute)]
use gba::prelude::*; use gba::{prelude::*, warn};
const BLACK: Color = Color::from_rgb(0, 0, 0); const BLACK: Color = Color::from_rgb(0, 0, 0);
const RED: Color = Color::from_rgb(31, 0, 0); const RED: Color = Color::from_rgb(31, 0, 0);
@ -70,6 +70,7 @@ fn main() -> ! {
flags = flags.with_timer1(true); flags = flags.with_timer1(true);
} }
warn!("IM = {:?}", flags);
unsafe { IE.write(flags) }; unsafe { IE.write(flags) };
// Puts the CPU into low power mode until a VBlank IRQ is received. This // Puts the CPU into low power mode until a VBlank IRQ is received. This
@ -98,53 +99,65 @@ extern "C" fn irq_handler_a32() {
} }
fn irq_handler_t32() { fn irq_handler_t32() {
let flags = IRQ_PENDING.read(); // disable Interrupt Master Enable to prevent an interrupt during the handler
unsafe { IME.write(false) };
if flags.vblank() { // read which interrupts are pending, and "filter" the selection by which are
// supposed to be enabled.
let which_interrupts_to_handle = IRQ_PENDING.read() & IE.read();
// read the current IntrWait value. It sorta works like a running total, so
// any interrupts we process we'll enable in this value, which we write back
// at the end.
let mut intr_wait_flags = INTR_WAIT_ACKNOWLEDGE.read();
if which_interrupts_to_handle.vblank() {
vblank_handler(); vblank_handler();
intr_wait_flags.set_vblank(true);
} }
if flags.hblank() { if which_interrupts_to_handle.hblank() {
hblank_handler(); hblank_handler();
intr_wait_flags.set_hblank(true);
} }
if flags.vcount() { if which_interrupts_to_handle.vcount() {
vcount_handler(); vcount_handler();
intr_wait_flags.set_vcount(true);
} }
if flags.timer0() { if which_interrupts_to_handle.timer0() {
timer0_handler(); timer0_handler();
intr_wait_flags.set_timer0(true);
} }
if flags.timer1() { if which_interrupts_to_handle.timer1() {
timer1_handler(); timer1_handler();
intr_wait_flags.set_timer1(true);
} }
// acknowledge that we did stuff.
IRQ_ACKNOWLEDGE.write(which_interrupts_to_handle);
// write out any IntrWait changes.
unsafe { INTR_WAIT_ACKNOWLEDGE.write(intr_wait_flags) };
// re-enable as we go out.
unsafe { IME.write(true) };
} }
fn vblank_handler() { fn vblank_handler() {
write_pixel(BLUE); write_pixel(BLUE);
// When using `interrupt_wait()` or `vblank_interrupt_wait()`, IRQ handlers must acknowledge
// the IRQ on the BIOS Interrupt Flags register.
unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_vblank(true)) };
} }
fn hblank_handler() { fn hblank_handler() {
write_pixel(GREEN); write_pixel(GREEN);
unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_hblank(true)) };
} }
fn vcount_handler() { fn vcount_handler() {
write_pixel(RED); write_pixel(RED);
unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_vcount(true)) };
} }
fn timer0_handler() { fn timer0_handler() {
write_pixel(YELLOW); write_pixel(YELLOW);
unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_timer0(true)) };
} }
fn timer1_handler() { fn timer1_handler() {
write_pixel(PINK); write_pixel(PINK);
unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_timer1(true)) };
} }

View file

@ -93,6 +93,60 @@ macro_rules! bitfield_bool {
} }
pub(crate) use bitfield_bool; pub(crate) use bitfield_bool;
/// Adds bitwise ops for this type
macro_rules! impl_bitwise_ops {
($outer:ty) => {
impl core::ops::Not for $outer {
type Output = Self;
#[inline]
fn not(self) -> Self {
Self(!self.0)
}
}
impl core::ops::BitAnd for $outer {
type Output = $outer;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl core::ops::BitOr for $outer {
type Output = $outer;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl core::ops::BitXor for $outer {
type Output = $outer;
#[inline]
fn bitxor(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
}
// // // // //
impl core::ops::BitAndAssign for $outer {
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0
}
}
impl core::ops::BitOrAssign for $outer {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
impl core::ops::BitXorAssign for $outer {
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0
}
}
};
}
pub(crate) use impl_bitwise_ops;
mod display_control; mod display_control;
pub use display_control::*; pub use display_control::*;

View file

@ -1,6 +1,6 @@
use super::*; use super::*;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[derive(Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)] #[repr(transparent)]
pub struct InterruptFlags(pub(crate) u16); pub struct InterruptFlags(pub(crate) u16);
impl InterruptFlags { impl InterruptFlags {
@ -20,4 +20,53 @@ impl InterruptFlags {
bitfield_bool!(u16; 12, keypad, with_keypad, set_keypad); bitfield_bool!(u16; 12, keypad, with_keypad, set_keypad);
bitfield_bool!(u16; 13, gamepak, with_gamepak, set_gamepak); bitfield_bool!(u16; 13, gamepak, with_gamepak, set_gamepak);
} }
// TODO: bit ops for interrupt flags impl_bitwise_ops!(InterruptFlags);
impl core::fmt::Debug for InterruptFlags {
#[inline(never)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "InterruptFlags {{")?;
if self.vblank() {
write!(f, "vblank,")?;
}
if self.hblank() {
write!(f, "hblank,")?;
}
if self.vcount() {
write!(f, "vcount,")?;
}
if self.timer0() {
write!(f, "timer0,")?;
}
if self.timer1() {
write!(f, "timer1,")?;
}
if self.timer2() {
write!(f, "timer2,")?;
}
if self.timer3() {
write!(f, "timer3,")?;
}
if self.serial() {
write!(f, "serial,")?;
}
if self.dma0() {
write!(f, "dma0,")?;
}
if self.dma1() {
write!(f, "dma1,")?;
}
if self.dma2() {
write!(f, "dma2,")?;
}
if self.dma3() {
write!(f, "dma3,")?;
}
if self.keypad() {
write!(f, "keypad,")?;
}
if self.gamepak() {
write!(f, "gamepak,")?;
}
write!(f, "}}")
}
}