Skip to content

Commit

Permalink
Merge pull request #51 from YushiOMOTE/support-apu-start-with-bit4-set
Browse files Browse the repository at this point in the history
Support apu start with div bit4 set
  • Loading branch information
YushiOMOTE authored Jul 29, 2024
2 parents 1927dcf + 6d6634c commit a0d14f0
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 79 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Test status of [Blargg's Gameboy hardware test ROMs](https://github.com/retrio/g
Test status of [Same Suite](https://github.com/YushiOMOTE/SameSuite/tree/430ab7f68fc612e005ed5586990dfec0ea7a9ce5)

* [x] `apu/div_write_trigger.gb`
* [ ] `apu/div_write_trigger_10.gb`
* [x] `apu/div_write_trigger_10.gb`

## Projects

Expand Down
6 changes: 4 additions & 2 deletions core/src/apu/envelope.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::clock::Timer;

use super::frame_sequencer::Frame;

#[derive(Debug, Clone)]
pub struct Envelope {
amp: usize,
Expand All @@ -23,8 +25,8 @@ impl Envelope {
self.timer.set_interval(count);
}

pub fn step(&mut self, frame: Option<usize>) {
match frame {
pub fn step(&mut self, frame: Frame) {
match frame.switched() {
Some(7) => {}
_ => return,
}
Expand Down
173 changes: 143 additions & 30 deletions core/src/apu/frame_sequencer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
use crate::clock::ClockDivider;

const FRAME_SEQUENCER_FREQ_HZ: usize = 512;

/// The frame sequencer generates low frequency clocks for the modulation units. It is clocked by a 512 Hz timer.
///
/// Step Length Ctr Vol Env Sweep
Expand All @@ -19,56 +15,173 @@ const FRAME_SEQUENCER_FREQ_HZ: usize = 512;
///
#[derive(Debug, Clone)]
pub struct FrameSequencer {
divider: ClockDivider,
step: usize,
frame: Frame,
resetting: usize,
last_div: usize,
}

#[derive(Debug, Clone, Copy)]
pub struct Frame {
pub step: usize,
pub cycles: usize,
}

impl Frame {
pub fn switched(&self) -> Option<usize> {
if self.cycles == 0 {
Some(self.step)
} else {
None
}
}
}

impl Frame {
fn new() -> Self {
Self { step: 7, cycles: 0 }
}
}

impl FrameSequencer {
pub fn new() -> Self {
Self {
divider: ClockDivider::new(FRAME_SEQUENCER_FREQ_HZ),
step: 0,
frame: Frame::new(),
resetting: 0,
last_div: 0,
}
}

pub fn reset_step(&mut self) {
self.step = 0;
self.frame = Frame::new();

// This is to prevent updaating step immediately to 1 after reset
// when reset and div-apu happens in the same machine cycle,
self.resetting = 4;
self.divider.reset();
}

pub fn step(&mut self, cycles: usize, div_apu: bool) -> Option<usize> {
if div_apu && self.resetting == 0 {
return Some(self.update());
fn bit_down(&mut self, div: usize) -> bool {
let bit4_old = bit4(self.last_div);
let bit4_new = bit4(div);

self.last_div = div;

bit4_old && !bit4_new
}

pub fn step(&mut self, cycles: usize, div: usize) -> Frame {
if self.resetting > 0 && div & 0x10 > 0 {
// If reset with bit 4 set, skip the first frame
self.update();
}

self.frame.cycles = self.frame.cycles.wrapping_add(cycles);

if self.bit_down(div) && self.resetting == 0 {
self.update();
}

self.resetting = self.resetting.saturating_sub(cycles);
None

self.frame
}

fn update(&mut self) -> usize {
let current_step = self.step;
self.step = (self.step + 1) % 8;
current_step
fn update(&mut self) {
let new_step = (self.frame.step + 1) % 8;
self.frame.step = new_step;
self.frame.cycles = 0;
}
}

fn bit4(value: usize) -> bool {
value & 0x10 != 0
}

#[test]
fn test_frame_sequencer_step() {
let mut seq = FrameSequencer::new();

assert_eq!(seq.step(1, 0x10).step, 7);
assert_eq!(seq.step(1, 0x00).step, 0);
assert_eq!(seq.step(1, 0x10).step, 0);
assert_eq!(seq.step(1, 0x00).step, 1);
assert_eq!(seq.step(1, 0x10).step, 1);
assert_eq!(seq.step(1, 0x00).step, 2);
assert_eq!(seq.step(1, 0x10).step, 2);
assert_eq!(seq.step(1, 0x00).step, 3);
assert_eq!(seq.step(1, 0x10).step, 3);
assert_eq!(seq.step(1, 0x00).step, 4);
assert_eq!(seq.step(1, 0x10).step, 4);
assert_eq!(seq.step(1, 0x00).step, 5);
assert_eq!(seq.step(1, 0x10).step, 5);
assert_eq!(seq.step(1, 0x00).step, 6);
assert_eq!(seq.step(1, 0x10).step, 6);
assert_eq!(seq.step(1, 0x00).step, 7);
assert_eq!(seq.step(1, 0x10).step, 7);
assert_eq!(seq.step(1, 0x00).step, 0);
assert_eq!(seq.step(1, 0x10).step, 0);
assert_eq!(seq.step(1, 0x00).step, 1);
assert_eq!(seq.step(1, 0x10).step, 1);
assert_eq!(seq.step(1, 0x00).step, 2);
}

#[test]
fn test_frame_sequencer_cycles() {
let mut seq = FrameSequencer::new();

assert_eq!(seq.step(1, 0x10).cycles, 1);
assert_eq!(seq.step(2, 0x10).cycles, 3);
assert_eq!(seq.step(3, 0x10).cycles, 6);
assert_eq!(seq.step(4, 0x10).cycles, 10);
assert_eq!(seq.step(5, 0x10).cycles, 15);
assert_eq!(seq.step(6, 0x00).step, 0);
assert_eq!(seq.step(7, 0x10).cycles, 7);
assert_eq!(seq.step(8, 0x10).cycles, 15);
assert_eq!(seq.step(9, 0x10).cycles, 24);
assert_eq!(seq.step(10, 0x10).cycles, 34);
assert_eq!(seq.step(11, 0x10).cycles, 45);
assert_eq!(seq.step(12, 0x00).step, 1);
assert_eq!(seq.step(13, 0x10).cycles, 13);
assert_eq!(seq.step(14, 0x10).cycles, 27);
assert_eq!(seq.step(15, 0x10).cycles, 42);
assert_eq!(seq.step(16, 0x10).cycles, 58);
assert_eq!(seq.step(17, 0x10).cycles, 75);
}

#[test]
fn test_frame_sequencer() {
fn test_frame_sequencer_switch() {
let mut seq = FrameSequencer::new();

assert_eq!(seq.step(1, true), Some(0));
assert_eq!(seq.step(1, true), Some(1));
assert_eq!(seq.step(1, true), Some(2));
assert_eq!(seq.step(1, true), Some(3));
assert_eq!(seq.step(1, true), Some(4));
assert_eq!(seq.step(1, true), Some(5));
assert_eq!(seq.step(1, true), Some(6));
assert_eq!(seq.step(1, true), Some(7));
assert_eq!(seq.step(1, true), Some(0));
assert_eq!(seq.step(1, true), Some(1));
assert_eq!(seq.step(1, true), Some(2));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(0));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(1));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(2));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(3));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(4));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(5));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(6));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(7));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(0));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(1));
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(2));
}
21 changes: 11 additions & 10 deletions core/src/apu/length_counter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use log::*;

use super::frame_sequencer::Frame;

#[derive(Clone, Debug)]
pub struct LengthCounter {
enable: bool,
Expand Down Expand Up @@ -87,16 +89,15 @@ impl LengthCounter {
self.length = self.base - value;
}

pub fn step(&mut self, step: Option<usize>) {
match step {
Some(0) | Some(2) | Some(4) | Some(6) => {
self.first_half = true;
}
Some(1) | Some(3) | Some(5) | Some(7) => {
self.first_half = false;
return;
}
_ => return,
pub fn step(&mut self, frame: Frame) {
self.first_half = match frame.step {
0 | 2 | 4 | 6 => true,
1 | 3 | 5 | 7 => false,
_ => unreachable!(),
};

if frame.cycles != 0 || !self.first_half {
return;
}

if self.enable {
Expand Down
29 changes: 17 additions & 12 deletions core/src/apu/mixer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use super::{frame_sequencer::FrameSequencer, noise::Noise, tone::Tone, wave::Wave};
use super::{
frame_sequencer::{Frame, FrameSequencer},
noise::Noise,
tone::Tone,
wave::Wave,
};
use crate::{cpu::CPU_FREQ_HZ, divider::Divider, hardware::Stream};
use alloc::sync::Arc;
use bitfield_struct::bitfield;
Expand Down Expand Up @@ -146,8 +151,8 @@ impl<T: VolumeUnit> Shared<T> {
}
}

fn step(&mut self, cycles: usize, step: Option<usize>) {
self.channel.lock().step(cycles, step);
fn step(&mut self, cycles: usize, frame: Frame) {
self.channel.lock().step(cycles, frame);
}

fn sync_channel(&self, channel: T) {
Expand All @@ -166,16 +171,16 @@ impl<T: VolumeUnit> Shared<T> {
trait VolumeUnit {
fn amp(&self) -> isize;

fn step(&mut self, rate: usize, step: Option<usize>);
fn step(&mut self, rate: usize, frame: Frame);
}

impl VolumeUnit for Tone {
fn amp(&self) -> isize {
self.amp()
}

fn step(&mut self, cycles: usize, step: Option<usize>) {
self.step(cycles, step);
fn step(&mut self, cycles: usize, frame: Frame) {
self.step(cycles, frame);
}
}

Expand All @@ -184,8 +189,8 @@ impl VolumeUnit for Wave {
self.amp()
}

fn step(&mut self, cycles: usize, step: Option<usize>) {
self.step(cycles, step);
fn step(&mut self, cycles: usize, frame: Frame) {
self.step(cycles, frame);
}
}

Expand All @@ -194,8 +199,8 @@ impl VolumeUnit for Noise {
self.amp()
}

fn step(&mut self, cycles: usize, step: Option<usize>) {
self.step(cycles, step);
fn step(&mut self, cycles: usize, frame: Frame) {
self.step(cycles, frame);
}
}

Expand Down Expand Up @@ -249,8 +254,8 @@ impl MixerStream {
while cycles > 0 {
let sub_cycles = cycles.max(4);

let div_apu = self.divider.step(sub_cycles);
let step = self.frame_sequencer.step(cycles, div_apu);
let div = self.divider.step(sub_cycles);
let step = self.frame_sequencer.step(cycles, div);

self.state.tones[0].step(sub_cycles, step);
self.state.tones[1].step(sub_cycles, step);
Expand Down
4 changes: 2 additions & 2 deletions core/src/apu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ impl Apu {
self.mixer.sync_noise(self.noise.clone());
}

pub fn step(&mut self, cycles: usize, div_apu: bool) {
let frame = self.frame_sequencer.step(cycles, div_apu);
pub fn step(&mut self, cycles: usize, div: usize) {
let frame = self.frame_sequencer.step(cycles, div);

for tone in &mut self.tones {
tone.step(cycles, frame);
Expand Down
4 changes: 2 additions & 2 deletions core/src/apu/noise.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{dac::Dac, envelope::Envelope, length_counter::LengthCounter};
use super::{dac::Dac, envelope::Envelope, frame_sequencer::Frame, length_counter::LengthCounter};
use crate::clock::{ClockDivider, Timer};

use bitfield_struct::bitfield;
Expand Down Expand Up @@ -150,7 +150,7 @@ impl Noise {
self.nr44.trigger()
}

pub fn step(&mut self, cycles: usize, frame: Option<usize>) {
pub fn step(&mut self, cycles: usize, frame: Frame) {
self.length_counter.step(frame);
self.envelope.step(frame);

Expand Down
6 changes: 4 additions & 2 deletions core/src/apu/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::clock::Timer;

use log::*;

use super::frame_sequencer::Frame;

#[derive(Clone, Debug)]
pub struct Sweep {
disabling_channel: bool,
Expand Down Expand Up @@ -69,8 +71,8 @@ impl Sweep {
self.subtract = subtract;
}

pub fn step(&mut self, frame: Option<usize>) -> Option<usize> {
match frame {
pub fn step(&mut self, frame: Frame) -> Option<usize> {
match frame.switched() {
Some(2) | Some(6) => {}
_ => return None,
}
Expand Down
Loading

0 comments on commit a0d14f0

Please sign in to comment.