Skip to content

Commit

Permalink
Fix apu div timings and sound length, add option to disable saving
Browse files Browse the repository at this point in the history
  • Loading branch information
Kanabenki committed Sep 16, 2023
1 parent 4eb2549 commit f73b2ed
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 33 deletions.
72 changes: 70 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ ciborium = "0.2.1"
nom = "7.1.3"
cookie-factory = "0.3.2"
thiserror = "1.0.44"
num = "0.4.1"
107 changes: 89 additions & 18 deletions src/gameboy/apu.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::ops::SubAssign;

use num::PrimInt;
use serde::{Deserialize, Serialize};

use super::cpu::Cpu;
Expand All @@ -14,7 +17,10 @@ struct ChannelToggles {

impl ChannelToggles {
fn value(&self) -> u8 {
self.ch_1 as u8 | (self.ch_2 as u8) << 1 | (self.ch_3 as u8) << 2 | (self.ch_4 as u8) << 3
((self.ch_4 as u8) << 3)
| ((self.ch_3 as u8) << 2)
| ((self.ch_2 as u8) << 1)
| (self.ch_1 as u8)
}

fn from_nibble(nibble: u8) -> Self {
Expand All @@ -39,6 +45,7 @@ struct SoundEnable {

impl SoundEnable {
fn value(&self) -> u8 {
// dbg!(&self);
(self.enable_apu as u8) << 7 | 0b111_0000 | self.channels.value()
}
}
Expand Down Expand Up @@ -156,7 +163,7 @@ impl WaveDutyTimerLen {
_ => unreachable!(),
};

self.len_timer = value & 0b11_1111;
self.len_timer = (!value & 0b11_1111) + 1;
}
}

Expand Down Expand Up @@ -224,6 +231,18 @@ struct Channel1 {
wavelen_ctrl: WavelenCtrl,
}

impl Channel1 {
fn reset(&mut self) {
*self = Self {
wave_duty_len_timer: WaveDutyTimerLen {
wave_duty: WaveDuty::W0,
len_timer: self.wave_duty_len_timer.len_timer,
},
..Default::default()
}
}
}

#[derive(Serialize, Deserialize, Debug, Default)]
struct Channel2 {
cycles: u16,
Expand All @@ -234,11 +253,23 @@ struct Channel2 {
wavelen_ctrl: WavelenCtrl,
}

impl Channel2 {
fn reset(&mut self) {
*self = Self {
wave_duty_len_timer: WaveDutyTimerLen {
wave_duty: WaveDuty::W0,
len_timer: self.wave_duty_len_timer.len_timer,
},
..Default::default()
}
}
}

#[derive(Serialize, Deserialize, Debug, Default)]
struct Channel3 {
enable: bool,
cycles: u16,
len_timer: u8,
len_timer: u16,
out_level: u8,
wavelen_ctrl: WavelenCtrl,
wave_pattern: [u8; 16],
Expand All @@ -248,6 +279,7 @@ struct Channel3 {
impl Channel3 {
fn reset(&mut self) {
*self = Self {
len_timer: self.len_timer,
wave_pattern: self.wave_pattern,
..Default::default()
};
Expand Down Expand Up @@ -293,6 +325,15 @@ struct Channel4 {
len_enable: bool,
}

impl Channel4 {
fn reset(&mut self) {
*self = Self {
len_timer: self.len_timer,
..Default::default()
}
}
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub(crate) struct Apu {
left_deltas: [i32; 12],
Expand Down Expand Up @@ -356,37 +397,43 @@ impl Apu {
}

pub(crate) fn inc_div(&mut self) {
if !self.sound_enable.enable_apu {
return;
}

// Tick sound length.
if self.div & 0b1 == 0 {
let tick_len = |len_timer: &mut u8, len_enable, ch_enable: &mut bool, limit| {
*len_timer = u8::min(*len_timer + 1, limit);
if *len_timer == limit && len_enable {
*ch_enable = false;
fn tick_len<N: PrimInt + SubAssign>(
len_timer: &mut N,
len_enable: bool,
ch_enable: &mut bool,
) {
if len_enable && *len_timer > N::zero() {
*len_timer -= N::one();
if *len_timer == N::zero() {
*ch_enable = false;
}
}
};
}
tick_len(
&mut self.ch_1.wave_duty_len_timer.len_timer,
self.ch_1.wavelen_ctrl.len_enable,
&mut self.sound_enable.channels.ch_1,
0b11_1111,
);
tick_len(
&mut self.ch_2.wave_duty_len_timer.len_timer,
self.ch_2.wavelen_ctrl.len_enable,
&mut self.sound_enable.channels.ch_2,
0b11_1111,
);
tick_len(
&mut self.ch_3.len_timer,
self.ch_3.wavelen_ctrl.len_enable,
&mut self.sound_enable.channels.ch_3,
u8::MAX,
&mut self.sound_enable.channels.ch_2,
);
tick_len(
&mut self.ch_4.len_timer,
self.ch_4.len_enable,
&mut self.sound_enable.channels.ch_4,
0b11_1111,
);
}

Expand Down Expand Up @@ -454,22 +501,40 @@ impl Apu {
self.ch_1.wavelen_ctrl.trigger = false;
self.ch_1.volume = self.ch_1.vol_env.initial;
self.ch_1.wave_idx = 0;
if self.ch_1.wave_duty_len_timer.len_timer == 0 {
self.ch_1.wave_duty_len_timer.len_timer = 0b100_0000;
}
self.sound_enable.channels.ch_1 = true;
}

if self.ch_2.wavelen_ctrl.trigger {
self.ch_2.wavelen_ctrl.trigger = false;
self.ch_2.volume = self.ch_2.vol_env.initial;
self.ch_2.wave_idx = 0;
if self.ch_2.wave_duty_len_timer.len_timer == 0 {
self.ch_2.wave_duty_len_timer.len_timer = 0b100_0000;
}
self.sound_enable.channels.ch_2 = true;
}

if self.ch_3.enable && self.ch_3.wavelen_ctrl.trigger {
self.ch_3.wavelen_ctrl.trigger = false;
self.ch_3.wave_pattern_index = 0;
if self.ch_3.len_timer == 0 {
self.ch_3.len_timer = 0b1_0000_0000;
}
self.sound_enable.channels.ch_3 = true;
}

if self.ch_4.trigger {
self.ch_4.trigger = false;
self.ch_4.volume = self.ch_4.vol_env.initial;
if self.ch_4.len_timer == 0 {
self.ch_4.len_timer = 0b100_0000;
}
self.sound_enable.channels.ch_4 = true;
}

let amp_ch1 = if self.sound_enable.channels.ch_1 {
self.ch_1.cycles += 1;
if self.ch_1.cycles == Self::WAVELEN_MAX {
Expand Down Expand Up @@ -608,13 +673,19 @@ impl Apu {

pub(crate) fn write(&mut self, address: u16, value: u8) {
// Ignore register writes when APU is turned off.
// On DMG models, timer registers are still writable.
if !self.sound_enable.enable_apu
&& address != Self::SOUND_ENABLE_ADDRESS
&& address != Self::CH1_WAVE_DUTY_TIMER_LEN_ADDRESS
&& address != Self::CH2_WAVE_DUTY_TIMER_LEN_ADDRESS
&& address != Self::CH3_LEN_TIMER_ADDRESS
&& address != Self::CH4_LEN_TIMER_ADDRESS
&& !(Self::CH3_WAVE_PATTERN_START_ADDRESS..=Self::CH3_WAVE_PATTERN_END_ADDRESS)
.contains(&address)
{
return;
}

match address {
Self::CH1_SWEEP_ADDRESS => self.ch_1.sweep.set_value(value),
Self::CH1_VOLUME_ENVELOPPE_ADDRESS => self.ch_1.vol_env.set_value(value),
Expand All @@ -636,7 +707,7 @@ impl Apu {
self.ch_3.enable = value & 0b1000_0000 != 0;
self.sound_enable.channels.ch_3 = self.ch_3.enable;
}
Self::CH3_LEN_TIMER_ADDRESS => self.ch_3.len_timer = value,
Self::CH3_LEN_TIMER_ADDRESS => self.ch_3.len_timer = (!value as u16) + 1,
Self::CH3_OUT_LEVEL_ADDRESS => self.ch_3.out_level = (value >> 5) & 0b11,
Self::CH3_WAVELEN_LOW_ADDRESS => self.ch_3.wavelen_ctrl.set_value_wavelen_l(value),
Self::CH3_WAVELEN_HIGH_CTRL_ADDRESS => {
Expand All @@ -647,7 +718,7 @@ impl Apu {
}

Self::CH4_UNUSED_ADDRESS => {}
Self::CH4_LEN_TIMER_ADDRESS => self.ch_4.len_timer = value & 0b11_1111,
Self::CH4_LEN_TIMER_ADDRESS => self.ch_4.len_timer = (!value & 0b11_1111) + 1,
Self::CH4_VOLUME_ENVELOPPE_ADDRESS => self.ch_4.vol_env.set_value(value),
Self::CH4_FREQ_RAND_ADDRESS => self.ch_4.freq_rand.set_value(value),
Self::CH4_CTRL_ADDRESS => {
Expand All @@ -660,10 +731,10 @@ impl Apu {
Self::SOUND_ENABLE_ADDRESS => {
self.sound_enable.enable_apu = value >> 7 != 0;
if !self.sound_enable.enable_apu {
self.ch_1 = Default::default();
self.ch_2 = Default::default();
self.ch_1.reset();
self.ch_2.reset();
self.ch_3.reset();
self.ch_4 = Default::default();
self.ch_4.reset();
self.master_vol_vin_pan = Default::default();
self.sound_panning = Default::default();
}
Expand Down
6 changes: 3 additions & 3 deletions src/gameboy/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ impl Timer {
let old_divider = self.divider;
self.divider = self.divider.wrapping_add(4);

// Bit 4 high to low
// DIV bit 4 high to low
let apu_inc_div =
(old_divider & 0b1_0000) != 0 && (self.divider & 0b1_0000) == 0 || self.apu_inc_div;
(old_divider & (1 << 12)) != 0 && (self.divider & (1 << 12)) == 0 || self.apu_inc_div;
self.apu_inc_div = false;

if !self.enabled {
Expand Down Expand Up @@ -225,7 +225,7 @@ impl Timer {
if self.divider & self.input_clock.bit() != 0 {
self.increase_counter();
}
if self.divider & 0b1_0000 != 0 {
if self.divider & (1 << 12) != 0 {
self.apu_inc_div = true;
}
self.divider = 0;
Expand Down
Loading

0 comments on commit f73b2ed

Please sign in to comment.