use std::mem;
use bitflags::bitflags;
use crate::memdev::{MemDevice, RootMemDevice};
bitflags! {
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, MemDevice)]
#[memdev(bitflags)]
pub struct InterruptFlags: u8 {
const VBLANK = 0b00001;
const STAT = 0b00010;
const TIMER = 0b00100;
const SERIAL = 0b01000;
const JOYPAD = 0b10000;
}
}
impl InterruptFlags {
pub fn handler_addr(self) -> u16 {
const FIRST_INTERRUPT: u16 = 0x40;
const INTERRUPT_GAP: u16 = 0x08;
assert!(
self.bits().count_ones() == 1,
"mut have exactly on interrupt handler set"
);
FIRST_INTERRUPT + self.bits().trailing_zeros() as u16 * INTERRUPT_GAP
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, MemDevice)]
#[memdev(passthrough)]
pub struct InterruptEnable(pub InterruptFlags);
pub trait InterruptContext {
type Interrupts: Interrupts;
fn interrupts(&self) -> &Self::Interrupts;
fn interrupts_mut(&mut self) -> &mut Self::Interrupts;
}
pub trait Interrupts {
fn queued(&self) -> InterruptFlags;
fn set_queued(&mut self, flags: InterruptFlags);
#[inline]
fn send(&mut self, flags: InterruptFlags) {
self.set_queued(self.queued() | flags);
}
#[inline]
fn clear(&mut self, flags: InterruptFlags) {
self.set_queued(self.queued() - flags);
}
fn enabled(&self) -> InterruptFlags;
fn set_enabled(&mut self, flags: InterruptFlags);
#[inline]
fn active(&self) -> InterruptFlags {
self.enabled() & self.queued()
}
}
#[repr(transparent)]
pub struct MemInterrupts<M>(M);
impl<M: RootMemDevice> MemInterrupts<M> {
#[inline]
pub fn wrap_ref<'a>(device: &'a M) -> &'a MemInterrupts<M> {
unsafe { mem::transmute(device) }
}
#[inline]
pub fn wrap_mut<'a>(device: &'a mut M) -> &'a mut MemInterrupts<M> {
unsafe { mem::transmute(device) }
}
}
impl<M: RootMemDevice> Interrupts for MemInterrupts<M> {
fn queued(&self) -> InterruptFlags {
InterruptFlags::from_bits_truncate(self.0.read_byte(0xff0f))
}
fn set_queued(&mut self, flags: InterruptFlags) {
self.0.write_byte(0xff0f, flags.bits());
}
fn enabled(&self) -> InterruptFlags {
InterruptFlags::from_bits_truncate(self.0.read_byte(0xffff))
}
fn set_enabled(&mut self, flags: InterruptFlags) {
self.0.write_byte(0xffff, flags.bits());
}
}