diff --git a/examples/irq.rs b/examples/irq.rs index 88e4ad1..ec1fa37 100644 --- a/examples/irq.rs +++ b/examples/irq.rs @@ -2,7 +2,7 @@ #![no_main] #![feature(isa_attribute)] -use gba::prelude::*; +use gba::{prelude::*, warn}; const BLACK: Color = Color::from_rgb(0, 0, 0); const RED: Color = Color::from_rgb(31, 0, 0); @@ -70,6 +70,7 @@ fn main() -> ! { flags = flags.with_timer1(true); } + warn!("IM = {:?}", flags); unsafe { IE.write(flags) }; // 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() { - 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(); + intr_wait_flags.set_vblank(true); } - if flags.hblank() { + if which_interrupts_to_handle.hblank() { hblank_handler(); + intr_wait_flags.set_hblank(true); } - if flags.vcount() { + if which_interrupts_to_handle.vcount() { vcount_handler(); + intr_wait_flags.set_vcount(true); } - if flags.timer0() { + if which_interrupts_to_handle.timer0() { timer0_handler(); + intr_wait_flags.set_timer0(true); } - if flags.timer1() { + if which_interrupts_to_handle.timer1() { 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() { 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() { write_pixel(GREEN); - - unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_hblank(true)) }; } fn vcount_handler() { write_pixel(RED); - - unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_vcount(true)) }; } fn timer0_handler() { write_pixel(YELLOW); - - unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_timer0(true)) }; } fn timer1_handler() { write_pixel(PINK); - - unsafe { INTR_WAIT_ACKNOWLEDGE.write(INTR_WAIT_ACKNOWLEDGE.read().with_timer1(true)) }; } diff --git a/src/mmio_types.rs b/src/mmio_types.rs index d4e938a..9a98578 100644 --- a/src/mmio_types.rs +++ b/src/mmio_types.rs @@ -93,6 +93,60 @@ macro_rules! 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; pub use display_control::*; diff --git a/src/mmio_types/interrupt_flags.rs b/src/mmio_types/interrupt_flags.rs index 2bac83e..ba12005 100644 --- a/src/mmio_types/interrupt_flags.rs +++ b/src/mmio_types/interrupt_flags.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] pub struct InterruptFlags(pub(crate) u16); impl InterruptFlags { @@ -20,4 +20,53 @@ impl InterruptFlags { bitfield_bool!(u16; 12, keypad, with_keypad, set_keypad); 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, "}}") + } +}