gameboy_core 0.3.3

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

pub struct WaveChannel {
    dac_enabled: bool,
    length_load: u8,
    timer_load: u16,
    length_enable: bool,
    trigger_bit: bool,
    volume_code: u8,
    wave_table: [u8; 16],
    length_counter: u16,
    enabled: bool,
    timer: i32,
    position_counter: u8,
    output_vol: u8,
}

impl WaveChannel {
    pub fn new() -> WaveChannel {
        WaveChannel {
            dac_enabled: false,
            length_load: 0,
            timer_load: 0,
            length_enable: false,
            trigger_bit: false,
            volume_code: 0,
            wave_table: [0; 16],
            length_counter: 0,
            enabled: false,
            timer: 0,
            position_counter: 0,
            output_vol: 0,
        }
    }

    pub fn step(&mut self) {
        self.timer -= 1;
        if self.timer <= 0 {
            self.timer = (2048 - self.timer_load as i32) * 2;
            self.position_counter = (self.position_counter + 1) & 0x1F;
            if self.enabled && self.dac_enabled {
                let position = self.position_counter / 2;
                let mut output_byte = self.wave_table[position as usize];
                let high_bit = !bit_utils::is_set(self.position_counter, 0);
                if high_bit {
                    output_byte >>= 4;
                }
                output_byte &= 0xF;
                if self.volume_code > 0 {
                    output_byte >>= self.volume_code - 1;
                } else {
                    output_byte = 0;
                }
                self.output_vol = output_byte;
            } else {
                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 read_byte(&self, address: u16) -> u8 {
        match address {
            0xFF1A => {
                let dac_enabled = if self.dac_enabled { 1 } else { 0 };
                dac_enabled << 7
            }
            0xFF1B => self.length_load,
            0xFF1C => self.volume_code << 5,
            0xFF1D => (self.timer_load & 0xFF) as u8,
            0xFF1E => {
                let length_enable = if self.length_enable { 1 } else { 0 };
                let trigger_bit = if self.trigger_bit { 1 } else { 0 };
                ((self.timer_load >> 8) & 0x07) as u8 | (length_enable << 6) | (trigger_bit << 7)
            }
            0xFF30..=0xFF3F => self.wave_table[(address - 0xFF30) as usize],
            _ => panic!("unknown address: {:04X}", address),
        }
    }

    pub fn write_byte(&mut self, address: u16, value: u8) {
        match address {
            0xFF1A => self.dac_enabled = bit_utils::is_set(value, 7),
            0xFF1B => self.length_load = value,
            0xFF1C => self.volume_code = (value >> 5) & 0x03,
            0xFF1D => self.timer_load = (self.timer_load & 0x700) | value as u16,
            0xFF1E => {
                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 self.trigger_bit {
                    self.trigger();
                }
            }
            0xFF30..=0xFF3F => self.wave_table[(address - 0xFF30) as usize] = value,
            _ => panic!("unknown address: {:04X}", address),
        }
    }

    fn trigger(&mut self) {
        self.enabled = true;
        if self.length_counter == 0 {
            self.length_counter = 256;
        }
        self.timer = (2048 - self.timer_load as i32) * 2;
        self.position_counter = 0;
    }

    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
    }
}