gameboy_core 0.3.3

core library to be used for Gameboy emulation
Documentation
use crate::bit_utils;

const DUTY_TABLE: [[bool; 8]; 4] = [
    [false, false, false, false, false, false, false, true],
    [true, false, false, false, false, false, false, true],
    [true, false, false, false, false, true, true, true],
    [false, true, true, true, true, true, true, false],
];

pub struct PulseChannel {
    sweep_shift: u8,
    sweep_negate: bool,
    sweep_period_load: u8,
    length_load: u8,
    duty: u8,
    envelope_period: i32,
    envelope_period_load: u8,
    envelope_add_mode: bool,
    volume_load: u8,
    volume: u8,
    timer_load: u16,
    length_enable: bool,
    trigger_bit: bool,
    length_counter: u8,
    dac_enabled: bool,
    enabled: bool,
    timer: i32,
    envelope_running: bool,
    sweep_shadow: u16,
    sweep_enable: bool,
    sweep_period: i32,
    output_vol: u8,
    sequence_pointer: u32,
}

impl PulseChannel {
    pub fn new() -> PulseChannel {
        PulseChannel {
            sweep_shift: 0,
            sweep_negate: false,
            sweep_period_load: 0,
            length_load: 0,
            duty: 0,
            envelope_period: 0,
            envelope_period_load: 0,
            envelope_add_mode: false,
            volume_load: 0,
            volume: 0,
            timer_load: 0,
            length_enable: false,
            trigger_bit: false,
            length_counter: 0,
            dac_enabled: true,
            timer: 0,
            sweep_enable: false,
            envelope_running: true,
            sweep_shadow: 0,
            enabled: false,
            sweep_period: 0,
            output_vol: 0,
            sequence_pointer: 0,
        }
    }

    pub fn step(&mut self) {
        self.timer -= 1;
        if self.timer <= 0 {
            self.timer = (2048 - self.timer_load as i32) * 4;
            self.sequence_pointer = (self.sequence_pointer + 1) & 0x07;
        }
        if self.enabled && self.dac_enabled {
            self.output_vol = self.volume;
        } else {
            self.output_vol = 0;
        }

        if !DUTY_TABLE[self.duty as usize][self.sequence_pointer as usize] {
            self.output_vol = 0;
        }
    }

    pub fn length_click(&mut self) {
        if self.length_counter > 0 && self.length_enable {
            self.length_counter -= 1;
            if self.length_counter == 0 {
                self.enabled = false;
            }
        }
    }

    pub fn sweep_click(&mut self) {
        self.sweep_period -= 1;
        if self.sweep_period <= 0 {
            self.sweep_period = self.sweep_period_load as i32;
            if self.sweep_period == 0 {
                self.sweep_period = 8;
            }
            if self.sweep_enable && self.sweep_period_load > 0 {
                let new_freq = self.sweep_calculation();
                if new_freq <= 2047 && self.sweep_shift > 0 {
                    self.sweep_shadow = new_freq;
                    self.timer_load = new_freq;
                    self.sweep_calculation();
                }
                self.sweep_calculation();
            }
        }
    }

    pub fn env_click(&mut self) {
        self.envelope_period -= 1;
        if self.envelope_period <= 0 {
            self.envelope_period = self.envelope_period_load as i32;
            if self.envelope_period == 0 {
                self.envelope_period = 8;
            }
            if self.envelope_running && self.envelope_period_load > 0 {
                if self.envelope_add_mode && self.volume < 15 {
                    self.volume += 1;
                } else if !self.envelope_add_mode && self.volume > 0 {
                    self.volume -= 1;
                }
            }
            if self.volume == 0 || self.volume == 15 {
                self.envelope_running = false;
            }
        }
    }

    pub fn read_byte(&self, address: u16) -> u8 {
        match (address & 0xFF & 0xF) % 0x5 {
            0x0 => {
                let sweep_negate = if self.sweep_negate { 1 } else { 0 };
                self.sweep_shift | (sweep_negate << 3) | (self.sweep_period_load << 4)
            }
            0x1 => (self.length_load & 0x3F) | ((self.duty & 0x03) << 6),
            0x2 => {
                let envelope_add_mode = if self.envelope_add_mode { 1 } else { 0 };
                (self.envelope_period_load & 0x07)
                    | (envelope_add_mode << 3)
                    | ((self.volume_load & 0x0F) << 4)
            }
            0x3 => (self.timer_load & 0xFF) as u8,
            0x4 => {
                let trigger_bit = if self.trigger_bit { 1 } else { 0 };
                let length_enable = if self.length_enable { 1 } else { 0 };
                ((self.timer_load >> 8) & 0x07) as u8 | (length_enable << 6) | (trigger_bit << 7)
            }
            _ => unreachable!(),
        }
    }

    pub fn write_byte(&mut self, address: u16, value: u8) {
        match (address & 0xFF & 0xF) % 0x5 {
            0x0 => {
                self.sweep_shift = value & 0x07;
                self.sweep_negate = bit_utils::is_set(value, 3);
                self.sweep_period_load = (value >> 4) & 0x07;
            }
            0x1 => {
                self.length_load = value & 0x3F;
                self.duty = (value >> 6) & 0x3;
            }
            0x2 => {
                self.dac_enabled = (value & 0xF8) != 0;
                self.volume_load = (value >> 4) & 0x0F;
                self.envelope_add_mode = bit_utils::is_set(value, 3);
                self.envelope_period_load = value & 0x7;
                self.envelope_period = self.envelope_period_load as i32;
                self.volume = self.volume_load;
            }
            0x3 => self.timer_load = (self.timer_load & 0x700) | value as u16,
            0x4 => {
                self.timer_load = (self.timer_load & 0xFF) | ((value as u16 & 0x7) << 8);
                self.length_enable = bit_utils::is_set(value, 6);
                self.trigger_bit = bit_utils::is_set(value, 7);
                if bit_utils::is_set(value, 7) {
                    self.trigger();
                }
            }
            _ => unreachable!(),
        }
    }

    fn trigger(&mut self) {
        self.enabled = true;
        if self.length_counter == 0 {
            self.length_counter = 64;
        }
        self.timer = (2048 - self.timer_load as i32) * 4;
        self.envelope_running = true;
        self.envelope_period = self.envelope_period_load as i32;
        self.volume = self.volume_load;
        self.sweep_shadow = self.timer_load;
        self.sweep_period = self.sweep_period_load as i32;
        if self.sweep_period == 0 {
            self.sweep_period = 8;
        }
        self.sweep_enable = self.sweep_period > 0 || self.sweep_shift > 0;
        if self.sweep_shift > 0 {
            self.sweep_calculation();
        }
    }

    fn sweep_calculation(&mut self) -> u16 {
        let mut new_freq = self.sweep_shadow >> self.sweep_shift;
        if self.sweep_negate {
            new_freq = self.sweep_shadow - new_freq;
        } else {
            new_freq += self.sweep_shadow;
        }
        if new_freq > 2047 {
            self.enabled = false;
        }
        new_freq
    }

    pub fn get_status(&self) -> bool {
        self.enabled && self.dac_enabled
    }

    pub fn reset_length_counter(&mut self) {
        self.length_counter = 0;
    }

    pub fn get_output_vol(&self) -> u8 {
        self.output_vol
    }
}