use save_state::Savable;
use super::ApuChannel;
use crate::GameBoyConfig;
const VOLUME_SHIFT_TABLE: [u8; 4] = [4, 0, 1, 2];
#[derive(Default, Savable)]
pub struct WaveChannel {
volume: u8,
volume_shift: u8,
frequency: u16,
buffer: [u8; 16],
buffer_position: u8,
buffer_position_just_clocked: bool,
frequency_timer: u16,
channel_enable: bool,
dac_enable: bool,
config: GameBoyConfig,
}
impl WaveChannel {
pub fn new(config: GameBoyConfig) -> Self {
Self {
config,
..Self::default()
}
}
pub fn write_volume(&mut self, vol: u8) {
self.volume = vol;
self.volume_shift = VOLUME_SHIFT_TABLE[vol as usize & 3];
}
pub fn read_volume(&self) -> u8 {
self.volume
}
pub fn frequency(&self) -> u16 {
self.frequency
}
pub fn write_frequency(&mut self, data: u16) {
self.frequency = data;
}
pub fn write_buffer(&mut self, offset: u8, data: u8) {
if let Some(index) = self.wave_buffer_index(offset) {
self.buffer[index] = data;
}
}
pub fn read_buffer(&self, offset: u8) -> u8 {
if let Some(index) = self.wave_buffer_index(offset) {
self.buffer[index]
} else {
0xFF
}
}
pub fn clock(&mut self) {
for _ in 0..2 {
self.buffer_position_just_clocked = false;
if self.frequency_timer == 0 {
self.clock_position();
self.buffer_position_just_clocked = true;
self.frequency_timer = 0x7FF - self.frequency;
} else {
self.frequency_timer -= 1;
}
}
}
pub fn reset_buffer_index(&mut self) {
self.buffer_position = 0;
}
}
impl WaveChannel {
fn clock_position(&mut self) {
self.buffer_position = (self.buffer_position + 1) & 0x1F;
}
fn wave_buffer_index(&self, offset: u8) -> Option<usize> {
let index = if self.dac_enable && self.channel_enable {
if self.config.is_dmg && !self.buffer_position_just_clocked {
return None;
}
self.buffer_position / 2
} else {
offset
} as usize
& 0xF;
Some(index)
}
}
impl ApuChannel for WaveChannel {
fn output(&self) -> u8 {
let byte = self.buffer[self.buffer_position as usize / 2];
let shift = 4 * ((self.buffer_position & 1) ^ 1);
let byte = (byte >> shift) & 0xF;
byte >> self.volume_shift
}
fn muted(&self) -> bool {
false
}
fn set_enable(&mut self, enabled: bool) {
self.channel_enable = enabled;
}
fn enabled(&self) -> bool {
self.channel_enable
}
fn trigger(&mut self) {
if self.config.is_dmg && self.frequency_timer == 0 {
let index = ((self.buffer_position + 1) & 0x1F) / 2;
if index < 4 {
self.buffer[0] = self.buffer[index as usize];
} else {
let four_bytes_align_start = ((index / 4) * 4) as usize;
for i in 0..4 {
self.buffer[i] = self.buffer[four_bytes_align_start + i];
}
}
}
self.buffer_position = 0;
self.frequency_timer = 0x7FF - self.frequency + 3;
}
fn set_dac_enable(&mut self, enabled: bool) {
self.dac_enable = enabled;
}
fn dac_enabled(&self) -> bool {
self.dac_enable
}
}