tetanes-core 0.14.0

A NES Emulator written in Rust
//! `VrcIrq`
//!
//! <https://www.nesdev.org/wiki/VRC_IRQ>

use crate::common::{Clock, Reset, ResetKind};
use serde::{Deserialize, Serialize};

#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
#[must_use]
pub struct VrcIrq {
    pub reload: u8,
    pub counter: u8,
    pub prescalar_counter: i16,
    pub irq_pending: bool,
    pub enabled: bool,
    pub enabled_after_ack: bool,
    pub cycle_mode: bool,
}

impl VrcIrq {
    pub const fn write_reload(&mut self, val: u8) {
        self.reload = val;
    }

    pub const fn write_control(&mut self, val: u8) {
        self.enabled_after_ack = val & 0x01 == 0x01;
        self.enabled = val & 0x02 == 0x02;
        self.cycle_mode = val & 0x04 == 0x04;

        if self.enabled {
            self.counter = self.reload;
            self.prescalar_counter = 341;
        }

        self.irq_pending = false;
    }

    pub const fn acknowledge(&mut self) {
        self.enabled = self.enabled_after_ack;
        self.irq_pending = false;
    }
}

impl Clock for VrcIrq {
    #[inline]
    fn clock(&mut self) {
        if self.enabled {
            self.prescalar_counter -= 3;
            if self.cycle_mode || self.prescalar_counter <= 0 {
                if self.counter == 0xFF {
                    self.counter = self.reload;
                    self.irq_pending = true;
                } else {
                    self.counter += 1;
                }
                self.prescalar_counter += 341;
            }
        }
    }
}

impl Reset for VrcIrq {
    fn reset(&mut self, _kind: ResetKind) {
        self.reload = 0;
        self.counter = 0;
        self.prescalar_counter = 0;
        self.enabled = false;
        self.enabled_after_ack = false;
        self.cycle_mode = false;
    }
}