use std::{cmp::min, collections::VecDeque};
use bitvec::prelude::*;
use log::{info, warn};
use meru_interface::{AudioBuffer, AudioSample};
use serde::{Deserialize, Serialize};
use crate::{
consts::AUDIO_SAMPLES_PER_SECOND,
context::{Interrupt, SoundDma, Timing},
util::{pack, trait_alias},
};
trait_alias!(pub trait Context = Timing + SoundDma + Interrupt);
#[derive(Serialize, Deserialize)]
pub struct Sound {
power_on: bool,
output_ratio_gb: u8, output_ratio_direct_sound: [bool; 2],
channel_ctrl: [ChannelCtrl; 2],
pulse: [Pulse; 2],
wave: Wave,
noise: Noise,
direct_sound: [DirectSound; 2],
bias_level: u16,
amplitude_resolution: u8,
prev_clock: u64,
freq_counter: u64,
frame_counter: u64,
sampling_counter: u32,
#[serde(skip)]
audio_buffer: AudioBuffer,
}
impl Sound {
pub fn new() -> Sound {
Sound {
power_on: false,
output_ratio_gb: 0,
output_ratio_direct_sound: [false; 2],
channel_ctrl: Default::default(),
pulse: [Pulse::new(true), Pulse::new(false)],
wave: Default::default(),
noise: Default::default(),
direct_sound: [DirectSound::new(0), DirectSound::new(1)],
bias_level: 0x200,
amplitude_resolution: 0,
prev_clock: 0,
freq_counter: 0,
frame_counter: 0,
sampling_counter: 0,
audio_buffer: Default::default(),
}
}
pub fn audio_buffer(&self) -> &AudioBuffer {
&self.audio_buffer
}
pub fn clear_buffer(&mut self) {
self.audio_buffer.samples.clear();
self.audio_buffer.sample_rate = AUDIO_SAMPLES_PER_SECOND;
}
fn set_power(&mut self, on: bool) {
if self.power_on == on {
return;
}
self.power_on = on;
self.channel_ctrl = Default::default();
let ch1_len = self.pulse[0].length;
let ch2_len = self.pulse[1].length;
let ch3_len = self.wave.length;
let ch4_len = self.noise.length;
self.pulse[0].reset();
self.pulse[1].reset();
self.wave.reset();
self.noise.reset();
self.pulse[0].length = ch1_len;
self.pulse[1].length = ch2_len;
self.wave.length = ch3_len;
self.noise.length = ch4_len;
}
pub fn timer_overflow(&mut self, ctx: &mut impl Context, timer_ch: u8) {
if !self.power_on {
return;
}
for sound_ch in 0..2 {
self.direct_sound[sound_ch].timer_overflow(ctx, timer_ch);
}
}
pub fn tick(&mut self, ctx: &mut impl Context) {
let now = ctx.now();
let elapsed = now - self.prev_clock;
self.prev_clock = now;
self.freq_counter += elapsed;
let ticks = self.freq_counter / 16;
self.freq_counter %= 16;
for _ in 0..ticks {
self.tick_1m(ctx);
}
}
fn tick_1m(&mut self, _ctx: &mut impl Context) {
if self.power_on {
self.frame_counter += 1;
let frame_seq_step = self.frame_counter / 2048;
let length_tick = frame_seq_step % 2 == 0;
let envelope_tick = frame_seq_step % 8 == 7;
let sweep_tick = frame_seq_step % 4 == 2;
self.pulse[0].tick(length_tick, envelope_tick, sweep_tick);
self.pulse[1].tick(length_tick, envelope_tick, false);
self.wave.tick(length_tick);
self.noise.tick(length_tick, envelope_tick);
}
const TICKS_PER_SECOND: u32 = 1024 * 1024;
self.sampling_counter += AUDIO_SAMPLES_PER_SECOND;
if self.sampling_counter >= TICKS_PER_SECOND {
self.sampling_counter -= TICKS_PER_SECOND;
let sample = self.mix_output();
self.audio_buffer.samples.push(sample);
}
}
fn mix_output(&mut self) -> AudioSample {
if !self.power_on {
return AudioSample::new(0, 0);
}
let ch_output = [
self.pulse[0].output(),
self.pulse[1].output(),
self.wave.output(),
self.noise.output(),
];
let mut cgb_output = [0, 0];
for (ch, output) in ch_output.iter().enumerate() {
for (lr, cgb_output) in cgb_output.iter_mut().enumerate() {
if self.channel_ctrl[lr].output_ch[ch] {
*cgb_output += *output as i32 * self.channel_ctrl[lr].volume as i32 / 8;
}
}
}
let mut agb_output = [0, 0];
for ch in 0..2 {
if let Some(output) = self.direct_sound[ch].current_output {
let output = output as i8 as i32 * 32;
for (agb_output, output_enable) in
agb_output.iter_mut().zip(self.direct_sound[ch].output)
{
if output_enable {
*agb_output += output;
}
}
}
}
let cgb_amp = if self.output_ratio_gb != 3 {
1 << self.output_ratio_gb
} else {
0
};
let output = [0, 1].map(|i| {
let agb_amp = self.output_ratio_direct_sound[i] as i32 + 1;
let output = cgb_output[i] * cgb_amp / 4 + agb_output[i] * agb_amp * 4 / 2;
(output * 2).clamp(-32768, 32767) as i16
});
AudioSample::new(output[0], output[1])
}
}
#[derive(Default, Debug, Serialize, Deserialize)]
struct ChannelCtrl {
volume: u8,
output_ch: [bool; 4],
}
#[derive(Debug, Default, Serialize, Deserialize)]
struct Pulse {
has_sweep_unit: bool,
sweep_period: u8,
sweep_negate: bool,
sweep_shift: u8,
duty: u8,
length: u8, initial_volume: u8,
envelope_inc: bool,
envelope_period: u8, frequency: u16, length_enable: bool,
on: bool,
current_volume: u8,
envelope_timer: u8,
sweep_enable: bool,
freq_calculated_in_negate_mode: bool,
current_frequency: u16,
sweep_timer: u8,
frequency_timer: u16,
phase: usize,
length_tick_in: bool,
prev_length_tick: bool,
envelope_tick_in: bool,
prev_envelope_tick: bool,
sweep_tick_in: bool,
prev_sweep_tick: bool,
}
impl Pulse {
fn new(has_sweep_unit: bool) -> Self {
Self {
has_sweep_unit,
..Default::default()
}
}
fn reset(&mut self) {
*self = Self::new(self.has_sweep_unit);
}
fn read(&mut self, regno: usize) -> u8 {
match regno {
0 => pack! {
4..=6 => self.sweep_period,
3 => self.sweep_negate,
0..=2 => self.sweep_shift,
},
1 => pack!(6..=7 => self.duty),
2 => pack! {
4..=7 => self.initial_volume,
3 => self.envelope_inc,
0..=2 => self.envelope_period,
},
3 => 0,
4 => pack!(6 => self.length_enable),
_ => unreachable!(),
}
}
fn write(&mut self, regno: usize, data: u8) {
match regno {
0 => {
let prev_sweep_negate = self.sweep_negate;
let v = data.view_bits::<Lsb0>();
self.sweep_period = v[4..=6].load();
self.sweep_negate = v[3];
self.sweep_shift = v[0..=2].load();
if self.sweep_enable
&& self.freq_calculated_in_negate_mode
&& (prev_sweep_negate && !self.sweep_negate)
{
self.on = false;
self.freq_calculated_in_negate_mode = false;
}
}
1 => {
let v = data.view_bits::<Lsb0>();
self.duty = v[6..=7].load();
self.length = 64 - v[0..=5].load::<u8>();
}
2 => {
let v = data.view_bits::<Lsb0>();
self.initial_volume = v[4..=7].load();
self.envelope_inc = v[3];
self.envelope_period = v[0..=2].load();
}
3 => self.frequency.view_bits_mut::<Lsb0>()[0..=7].store(data),
4 => {
let v = data.view_bits::<Lsb0>();
self.frequency.view_bits_mut::<Lsb0>()[8..=10].store(v[0..=2].load::<u16>());
let prev_length_enable = self.length_enable;
self.length_enable = v[6];
if self.length_tick_in && !prev_length_enable && self.length_enable {
self.length_tick();
}
if v[7] {
self.trigger();
}
}
_ => unreachable!(),
}
if !self.dac_enable() {
self.on = false;
}
}
fn dac_enable(&self) -> bool {
self.initial_volume > 0 || self.envelope_inc
}
fn trigger(&mut self) {
self.on = self.dac_enable();
self.freq_calculated_in_negate_mode = false;
if self.length == 0 {
self.length = 64;
if self.length_tick_in && self.length_enable {
self.length_tick();
}
}
self.frequency_timer = 2048 - self.frequency;
self.envelope_timer = if self.envelope_period == 0 {
8
} else {
self.envelope_period
};
self.current_volume = self.initial_volume;
self.sweep_timer = self.sweep_period();
self.sweep_enable = self.sweep_period != 0 || self.sweep_shift != 0;
self.current_frequency = self.frequency;
if self.sweep_shift != 0 && self.new_freq() > 2047 {
self.on = false;
}
}
fn new_freq(&mut self) -> u16 {
if self.sweep_negate {
self.freq_calculated_in_negate_mode = true;
self.current_frequency - (self.current_frequency >> self.sweep_shift)
} else {
self.current_frequency + (self.current_frequency >> self.sweep_shift)
}
}
fn sweep_period(&self) -> u8 {
if self.sweep_period == 0 {
8
} else {
self.sweep_period
}
}
fn tick(&mut self, length_tick: bool, envelope_tick: bool, sweep_tick: bool) {
self.frequency_timer = self.frequency_timer.saturating_sub(1);
if self.frequency_timer == 0 {
self.frequency_timer = 2048 - self.frequency;
self.phase = (self.phase + 1) % 8;
}
self.length_tick_in = length_tick;
self.envelope_tick_in = envelope_tick;
self.sweep_tick_in = sweep_tick;
self.update_tick();
}
fn update_tick(&mut self) {
let length_tick = self.length_tick_in;
if !self.prev_length_tick && length_tick && self.length_enable {
self.length_tick();
}
self.prev_length_tick = length_tick;
let envelope_tick = self.envelope_tick_in;
if !self.prev_envelope_tick && envelope_tick {
self.envelope_tick();
}
self.prev_envelope_tick = envelope_tick;
let sweep_tick = self.sweep_tick_in;
if !self.prev_sweep_tick && sweep_tick {
self.sweep_tick();
}
self.prev_sweep_tick = sweep_tick;
}
fn length_tick(&mut self) {
self.length = self.length.saturating_sub(1);
if self.length == 0 {
self.on = false;
}
}
fn envelope_tick(&mut self) {
if self.envelope_timer > 0 {
self.envelope_timer -= 1;
if self.envelope_timer == 0 && self.envelope_period > 0 {
self.envelope_timer = self.envelope_period;
if self.envelope_inc {
self.current_volume = min(15, self.current_volume + 1);
} else {
self.current_volume = self.current_volume.saturating_sub(1);
}
}
}
}
fn sweep_tick(&mut self) {
let prev_timer = self.sweep_timer;
self.sweep_timer = self.sweep_timer.saturating_sub(1);
if prev_timer > 0 && self.sweep_timer == 0 {
self.sweep_timer = self.sweep_period();
if self.sweep_enable && self.sweep_period > 0 {
let new_freq = self.new_freq();
if new_freq <= 2047 && self.sweep_shift > 0 {
self.current_frequency = new_freq;
self.frequency = new_freq;
}
if self.new_freq() > 2047 {
self.on = false;
}
}
}
}
fn output(&self) -> i16 {
const WAVEFORM: [[u8; 8]; 4] = [
[0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 0],
];
if !self.on {
0
} else {
(WAVEFORM[self.duty as usize][self.phase as usize] as i16 * 2 - 1)
* self.current_volume as i16
* 256
}
}
}
#[derive(Default, Serialize, Deserialize)]
struct Wave {
enable: bool,
length: u16, output_level: u8, output_level_75: bool, frequency: u16,
length_enable: bool,
ram: [u8; 0x20],
ram_bank: bool, steps: bool,
on: bool,
frequency_timer: u16,
sample_latch: u8,
pos: u8,
length_tick_in: bool,
prev_length_tick: bool,
}
impl Wave {
fn reset(&mut self) {
let t = self.ram;
*self = Default::default();
self.ram = t;
}
fn read_ram(&mut self, addr: u32) -> u8 {
self.ram[(1 - self.ram_bank as usize) * 0x10 + addr as usize]
}
fn write_ram(&mut self, addr: u32, data: u8) {
self.ram[(1 - self.ram_bank as usize) * 0x10 + addr as usize] = data;
}
fn read(&mut self, regno: usize) -> u8 {
match regno {
0 => pack! {
5 => self.steps,
6 => self.ram_bank,
7 => self.enable,
},
1 => 0,
2 => pack! {
5..=6 => self.output_level,
7 => self.output_level_75,
},
3 => 0,
4 => pack!(6 => self.length_enable),
_ => unreachable!(),
}
}
fn write(&mut self, regno: usize, data: u8) {
match regno {
0 => {
let v = data.view_bits::<Lsb0>();
self.steps = v[5];
self.ram_bank = v[6];
self.enable = v[7];
}
1 => self.length = 256 - data as u16,
2 => {
let v = data.view_bits::<Lsb0>();
self.output_level = v[5..=6].load();
self.output_level_75 = v[7];
}
3 => self.frequency.view_bits_mut::<Lsb0>()[0..=7].store(data),
4 => {
let v = data.view_bits::<Lsb0>();
self.frequency.view_bits_mut::<Lsb0>()[8..=10].store(v[0..=2].load::<u16>());
self.length_enable = v[6];
self.update_tick();
if v[7] {
self.trigger();
}
}
_ => unreachable!(),
}
if !self.dac_enable() {
self.on = false;
}
self.update_tick();
}
fn dac_enable(&self) -> bool {
self.enable
}
fn trigger(&mut self) {
self.on = self.dac_enable();
if self.length == 0 {
self.length = 256;
}
self.frequency_timer = 2048 - self.frequency;
self.pos = 0;
}
fn tick(&mut self, length_tick: bool) {
for _ in 0..2 {
self.frequency_timer = self.frequency_timer.saturating_sub(1);
if self.frequency_timer == 0 {
self.frequency_timer = 2048 - self.frequency;
self.pos = (self.pos + 1) % 64;
let pos = if !self.steps {
self.ram_bank as u8 * 32 + self.pos % 32
} else {
(self.ram_bank as u8 * 32 + self.pos) % 64
};
let v = self.ram[pos as usize / 2];
self.sample_latch = if pos % 2 == 0 { v >> 4 } else { v & 0x0F };
}
}
self.length_tick_in = length_tick;
self.update_tick();
}
fn update_tick(&mut self) {
loop {
let length_tick = self.length_tick_in && self.length_enable && self.length > 0;
if self.prev_length_tick == length_tick {
break;
}
if !self.prev_length_tick && length_tick {
self.length_tick();
}
self.prev_length_tick = length_tick;
}
}
fn length_tick(&mut self) {
if self.length_enable {
self.length = self.length.saturating_sub(1);
if self.length == 0 {
self.on = false;
}
}
}
fn output(&self) -> i16 {
if !self.on {
0
} else {
let amp = if self.output_level_75 {
3
} else if self.output_level == 0 {
0
} else if self.output_level == 1 {
4
} else if self.output_level == 2 {
2
} else {
1
};
(self.sample_latch as i16 * 2 - 15) * 128 * amp / 4
}
}
}
#[derive(Default, Serialize, Deserialize)]
struct Noise {
length: u8, initial_volume: u8,
envelope_inc: bool,
envelope_period: u8, clock_shift: u8,
lsfr_width: bool, divisor_code: u8, length_enable: bool,
on: bool,
current_volume: u8,
envelope_timer: u8,
divisor_timer: u8,
shift_clock_timer: u16,
lsfr: u16,
sample_acc: usize,
sample_count: usize,
length_tick_in: bool,
prev_length_tick: bool,
envelope_tick_in: bool,
prev_envelope_tick: bool,
}
static DIVISOR: [u8; 8] = [8, 16, 32, 48, 64, 80, 96, 112];
impl Noise {
fn reset(&mut self) {
*self = Self::default();
}
fn read(&mut self, regno: usize) -> u8 {
match regno {
1 => 0,
2 => pack! {
4..=7 => self.initial_volume,
3 => self.envelope_inc,
0..=2 => self.envelope_period,
},
3 => pack! {
4..=7 => self.clock_shift,
3 => self.lsfr_width,
0..=2 => self.divisor_code,
},
4 => pack!(6 => self.length_enable),
_ => unreachable!(),
}
}
fn write(&mut self, regno: usize, data: u8) {
match regno {
1 => self.length = 64 - data.view_bits::<Lsb0>()[0..=5].load::<u8>(),
2 => {
let v = data.view_bits::<Lsb0>();
self.initial_volume = v[4..=7].load();
self.envelope_inc = v[3];
self.envelope_period = v[0..=2].load();
}
3 => {
let v = data.view_bits::<Lsb0>();
self.clock_shift = v[4..=7].load();
self.lsfr_width = v[3];
self.divisor_code = v[0..=2].load();
}
4 => {
let v = data.view_bits::<Lsb0>();
self.length_enable = v[6];
self.update_tick();
if v[7] {
self.trigger();
}
}
_ => unreachable!(),
}
if !self.dac_enable() {
self.on = false;
}
self.update_tick();
}
fn dac_enable(&self) -> bool {
self.initial_volume > 0 || self.envelope_inc
}
fn trigger(&mut self) {
self.on = self.dac_enable();
if self.length == 0 {
self.length = 64;
}
self.current_volume = self.initial_volume;
self.lsfr = 0x7FFF;
self.divisor_timer = DIVISOR[self.divisor_code as usize] / 8;
self.shift_clock_timer = 1 << (self.clock_shift + 1);
}
fn tick(&mut self, length_tick: bool, envelope_tick: bool) {
self.shift_clock_timer = self.shift_clock_timer.saturating_sub(1);
if self.shift_clock_timer == 0 {
self.shift_clock_timer = 1 << (self.clock_shift + 1);
self.divisor_timer = self.divisor_timer.saturating_sub(1);
if self.divisor_timer == 0 {
self.sample_acc += ((self.lsfr & 1) ^ 1) as usize;
self.sample_count += 1;
self.divisor_timer = DIVISOR[self.divisor_code as usize] / 8;
let b = (self.lsfr & 1) ^ ((self.lsfr >> 1) & 1);
self.lsfr = if !self.lsfr_width {
(self.lsfr >> 1) | (b << 14)
} else {
((self.lsfr >> 1) & !(1 << 6)) | (b << 6)
};
}
}
self.length_tick_in = length_tick;
self.envelope_tick_in = envelope_tick;
self.update_tick();
}
fn update_tick(&mut self) {
loop {
let length_tick = self.length_tick_in && self.length_enable && self.length > 0;
if self.prev_length_tick == length_tick {
break;
}
if !self.prev_length_tick && length_tick {
self.length_tick();
}
self.prev_length_tick = length_tick;
}
if !self.prev_envelope_tick && self.envelope_tick_in {
self.envelope_tick();
}
self.prev_envelope_tick = self.envelope_tick_in;
}
fn length_tick(&mut self) {
if self.length_enable {
self.length = self.length.saturating_sub(1);
if self.length == 0 {
self.on = false;
}
}
}
fn envelope_tick(&mut self) {
self.envelope_timer = self.envelope_timer.saturating_sub(1);
if self.envelope_timer == 0 && self.envelope_period > 0 {
self.envelope_timer = self.envelope_period;
if self.envelope_inc {
self.current_volume = min(15, self.current_volume + 1);
} else {
self.current_volume = self.current_volume.saturating_sub(1);
}
}
}
fn output(&mut self) -> i16 {
if !self.on {
0
} else {
let sample_acc = self.sample_acc + ((self.lsfr & 1) ^ 1) as usize;
let sample_count = self.sample_count + 1;
self.sample_acc = 0;
self.sample_count = 0;
((sample_acc as i32 * 512 / sample_count as i32 - 256) * self.current_volume as i32)
as i16
}
}
}
#[derive(Serialize, Deserialize)]
struct DirectSound {
ch: u8,
output: [bool; 2],
timer_ch: u8,
current_output: Option<u8>,
fifo: VecDeque<u8>,
}
impl DirectSound {
fn new(ch: u8) -> Self {
DirectSound {
ch,
output: [false, false],
timer_ch: 0,
current_output: None,
fifo: VecDeque::new(),
}
}
fn reset(&mut self) {
self.fifo.clear();
self.current_output = None;
}
fn push_fifo(&mut self, data: u8) {
while self.fifo.len() + 1 > 32 {
self.fifo.pop_front();
}
self.fifo.push_back(data);
}
fn timer_overflow(&mut self, ctx: &mut impl Context, timer_ch: u8) {
if timer_ch != self.timer_ch {
return;
}
self.current_output = self.fifo.pop_front();
if self.current_output.is_none() && (self.output[0] || self.output[1]) {
info!("Direct sound {}: FIFO empty", self.ch,);
}
if self.fifo.len() <= 16 {
ctx.set_sound_dma_request(self.ch, true);
}
}
}
impl Sound {
pub fn read(&mut self, _ctx: &mut impl Context, addr: u32) -> Option<u8> {
Some(match addr {
0x060 => self.pulse[0].read(0),
0x061 => 0,
0x062 => self.pulse[0].read(1),
0x063 => self.pulse[0].read(2),
0x064 => self.pulse[0].read(3),
0x065 => self.pulse[0].read(4),
0x066..=0x067 => 0,
0x068 => self.pulse[1].read(1),
0x069 => self.pulse[1].read(2),
0x06A..=0x06B => 0,
0x06C => self.pulse[1].read(3),
0x06D => self.pulse[1].read(4),
0x06E..=0x06F => 0,
0x070 => self.wave.read(0),
0x071 => 0,
0x072 => self.wave.read(1),
0x073 => self.wave.read(2),
0x074 => self.wave.read(3),
0x075 => self.wave.read(4),
0x076..=0x077 => 0,
0x078 => self.noise.read(1),
0x079 => self.noise.read(2),
0x07A..=0x07B => 0,
0x07C => self.noise.read(3),
0x07D => self.noise.read(4),
0x07E..=0x07F => 0,
0x080 => pack! {
0..=2 => self.channel_ctrl[0].volume,
4..=6 => self.channel_ctrl[1].volume,
},
0x081 => pack! {
7 => self.channel_ctrl[1].output_ch[3],
6 => self.channel_ctrl[1].output_ch[2],
5 => self.channel_ctrl[1].output_ch[1],
4 => self.channel_ctrl[1].output_ch[0],
3 => self.channel_ctrl[0].output_ch[3],
2 => self.channel_ctrl[0].output_ch[2],
1 => self.channel_ctrl[0].output_ch[1],
0 => self.channel_ctrl[0].output_ch[0],
},
0x082 => pack! {
0..=1 => self.output_ratio_gb,
2 => self.output_ratio_direct_sound[0],
3 => self.output_ratio_direct_sound[1],
},
0x083 => {
let mut data = 0;
let v = data.view_bits_mut::<Lsb0>();
for ch in 0..2 {
v.set(ch * 4, self.direct_sound[ch].output[0]);
v.set(ch * 4 + 1, self.direct_sound[ch].output[1]);
v.set(ch * 4 + 2, self.direct_sound[ch].timer_ch != 0);
}
data
}
0x084 => pack! {
7 => self.power_on,
4..=6 => 0,
3 => self.noise.on,
2 => self.wave.on,
1 => self.pulse[1].on,
0 => self.pulse[0].on,
},
0x085..=0x087 => return None,
0x088 => self.bias_level as u8,
0x089 => pack! {
0..=1 => self.bias_level >> 8,
6..=7 => self.amplitude_resolution,
},
0x08A..=0x08F => return None,
0x090..=0x09F => self.wave.read_ram(addr & 0xF),
0x0A0..=0x0AF => return None,
_ => unreachable!(),
})
}
pub fn write(&mut self, _ctx: &mut impl Context, addr: u32, data: u8) {
match addr {
0x060 => self.pulse[0].write(0, data),
0x061 => {}
0x062 => self.pulse[0].write(1, data),
0x063 => self.pulse[0].write(2, data),
0x064 => self.pulse[0].write(3, data),
0x065 => self.pulse[0].write(4, data),
0x066..=0x067 => {}
0x068 => self.pulse[1].write(1, data),
0x069 => self.pulse[1].write(2, data),
0x06A..=0x06B => {}
0x06C => self.pulse[1].write(3, data),
0x06D => self.pulse[1].write(4, data),
0x06E..=0x06F => {}
0x070 => self.wave.write(0, data),
0x071 => {}
0x072 => self.wave.write(1, data),
0x073 => self.wave.write(2, data),
0x074 => self.wave.write(3, data),
0x075 => self.wave.write(4, data),
0x076..=0x077 => {}
0x078 => self.noise.write(1, data),
0x079 => self.noise.write(2, data),
0x07A..=0x07B => {}
0x07C => self.noise.write(3, data),
0x07D => self.noise.write(4, data),
0x07E..=0x07F => {}
0x080 => {
let v = data.view_bits::<Lsb0>();
self.channel_ctrl[0].volume = v[0..=2].load();
self.channel_ctrl[1].volume = v[4..=6].load();
}
0x081 => {
let v = data.view_bits::<Lsb0>();
self.channel_ctrl[0].output_ch[0] = v[0];
self.channel_ctrl[0].output_ch[1] = v[1];
self.channel_ctrl[0].output_ch[2] = v[2];
self.channel_ctrl[0].output_ch[3] = v[3];
self.channel_ctrl[1].output_ch[0] = v[4];
self.channel_ctrl[1].output_ch[1] = v[5];
self.channel_ctrl[1].output_ch[2] = v[6];
self.channel_ctrl[1].output_ch[3] = v[7];
}
0x082 => {
let v = data.view_bits::<Lsb0>();
self.output_ratio_gb = v[0..=1].load();
self.output_ratio_direct_sound[0] = v[2];
self.output_ratio_direct_sound[1] = v[3];
if self.output_ratio_gb == 3 {
warn!("Invalid Output Ratio for GB sound");
}
}
0x083 => {
let v = data.view_bits::<Lsb0>();
for ch in 0..2 {
self.direct_sound[ch].output[0] = v[ch * 4];
self.direct_sound[ch].output[1] = v[ch * 4 + 1];
self.direct_sound[ch].timer_ch = v[ch * 4 + 2] as u8;
if v[ch * 4 + 3] {
self.direct_sound[ch].reset();
}
}
}
0x084 => {
self.set_power(data.view_bits::<Lsb0>()[7]);
}
0x085..=0x087 => {}
0x088 => self.bias_level.view_bits_mut::<Lsb0>()[0..=7].store(data),
0x089 => {
self.bias_level.view_bits_mut::<Lsb0>()[8..=9].store(data & 3);
self.amplitude_resolution = data >> 6;
}
0x08A..=0x08F => {}
0x090..=0x09F => self.wave.write_ram(addr & 0xF, data),
0x0A0..=0x0A3 => self.direct_sound[0].push_fifo(data),
0x0A4..=0x0A7 => self.direct_sound[1].push_fifo(data),
0x0A8..=0x0AF => {}
_ => unreachable!(),
}
}
}