Skip to content

Commit

Permalink
Merge pull request #55 from YushiOMOTE/div-write-trigger-volume
Browse files Browse the repository at this point in the history
Add div write trigger volume rom tests
  • Loading branch information
YushiOMOTE authored Jul 31, 2024
2 parents 56114cd + cd5da4d commit 05f6400
Show file tree
Hide file tree
Showing 8 changed files with 465 additions and 70 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Test status of [Same Suite](https://github.com/YushiOMOTE/SameSuite/tree/430ab7f

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

## Projects

Expand Down
186 changes: 133 additions & 53 deletions core/src/apu/frame_sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,37 @@ pub struct FrameSequencer {

#[derive(Debug, Clone, Copy)]
pub struct Frame {
pub step: usize,
pub last: Option<usize>,
pub next: Option<usize>,
pub cycles: usize,
}

impl Frame {
fn new() -> Self {
Self {
last: None,
next: Some(0),
cycles: 0,
}
}

fn partial() -> Self {
Self {
last: None,
next: None,
cycles: 0,
}
}

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

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

impl FrameSequencer {
pub fn new() -> Self {
Self {
Expand Down Expand Up @@ -70,8 +81,7 @@ impl FrameSequencer {

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 = Frame::partial();
}

self.frame.cycles = self.frame.cycles.wrapping_add(cycles);
Expand All @@ -86,8 +96,8 @@ impl FrameSequencer {
}

fn update(&mut self) {
let new_step = (self.frame.step + 1) % 8;
self.frame.step = new_step;
self.frame.last = self.frame.next;
self.frame.next = self.frame.next.map(|index| (index + 1) % 8).or(Some(0));
self.frame.cycles = 0;
}
}
Expand All @@ -97,54 +107,68 @@ fn bit4(value: usize) -> bool {
}

#[test]
fn test_frame_sequencer_step() {
fn test_frame_sequencer() {
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);
let f0 = seq.step(1, 0x10);

assert_eq!(f0.last, None);
assert_eq!(f0.next, Some(0));
assert_eq!(f0.cycles, 1);
assert_eq!(f0.switched(), None);

let f1 = seq.step(2, 0x10);

assert_eq!(f1.last, None);
assert_eq!(f1.next, Some(0));
assert_eq!(f1.cycles, 3);
assert_eq!(f1.switched(), None);

let f2 = seq.step(3, 0x10);

assert_eq!(f2.last, None);
assert_eq!(f2.next, Some(0));
assert_eq!(f2.cycles, 6);
assert_eq!(f2.switched(), None);

let f3 = seq.step(4, 0x00);

assert_eq!(f3.last, Some(0));
assert_eq!(f3.next, Some(1));
assert_eq!(f3.cycles, 0);
assert_eq!(f3.switched(), Some(0));

let f4 = seq.step(5, 0x10);

assert_eq!(f4.last, Some(0));
assert_eq!(f4.next, Some(1));
assert_eq!(f4.cycles, 5);
assert_eq!(f4.switched(), None);
}

#[test]
fn test_frame_sequencer_cycles() {
fn test_frame_sequencer_loop() {
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);
assert_eq!(seq.step(1, 0x10).next, Some(0));
assert_eq!(seq.step(1, 0x00).next, Some(1));
assert_eq!(seq.step(1, 0x10).next, Some(1));
assert_eq!(seq.step(1, 0x00).next, Some(2));
assert_eq!(seq.step(1, 0x10).next, Some(2));
assert_eq!(seq.step(1, 0x00).next, Some(3));
assert_eq!(seq.step(1, 0x10).next, Some(3));
assert_eq!(seq.step(1, 0x00).next, Some(4));
assert_eq!(seq.step(1, 0x10).next, Some(4));
assert_eq!(seq.step(1, 0x00).next, Some(5));
assert_eq!(seq.step(1, 0x10).next, Some(5));
assert_eq!(seq.step(1, 0x00).next, Some(6));
assert_eq!(seq.step(1, 0x10).next, Some(6));
assert_eq!(seq.step(1, 0x00).next, Some(7));
assert_eq!(seq.step(1, 0x10).next, Some(7));
assert_eq!(seq.step(1, 0x00).next, Some(0));
assert_eq!(seq.step(1, 0x10).next, Some(0));
assert_eq!(seq.step(1, 0x00).next, Some(1));
assert_eq!(seq.step(1, 0x10).next, Some(1));
}

#[test]
Expand Down Expand Up @@ -185,3 +209,59 @@ fn test_frame_sequencer_switch() {
assert_eq!(seq.step(1, 0x10).switched(), None);
assert_eq!(seq.step(1, 0x00).switched(), Some(2));
}

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

seq.reset_step();

let f0 = seq.step(4, 0x10);

assert_eq!(f0.last, None);
assert_eq!(f0.next, None);
assert_eq!(f0.cycles, 4);
assert_eq!(f0.switched(), None);

let f1 = seq.step(4, 0x10);

assert_eq!(f1.last, None);
assert_eq!(f1.next, None);
assert_eq!(f1.cycles, 8);
assert_eq!(f1.switched(), None);

let f2 = seq.step(4, 0x00);

assert_eq!(f2.last, None);
assert_eq!(f2.next, Some(0));
assert_eq!(f2.cycles, 0);
assert_eq!(f2.switched(), None); // First flip is ignored
}

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

seq.reset_step();

let f0 = seq.step(4, 0x00);

assert_eq!(f0.last, None);
assert_eq!(f0.next, Some(0));
assert_eq!(f0.cycles, 4);
assert_eq!(f0.switched(), None);

let f1 = seq.step(4, 0x10);

assert_eq!(f1.last, None);
assert_eq!(f1.next, Some(0));
assert_eq!(f1.cycles, 8);
assert_eq!(f1.switched(), None);

let f2 = seq.step(4, 0x00);

assert_eq!(f2.last, Some(0));
assert_eq!(f2.next, Some(1));
assert_eq!(f2.cycles, 0);
assert_eq!(f2.switched(), Some(0));
}
26 changes: 15 additions & 11 deletions core/src/apu/length_counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct LengthCounter {
active: bool,
length: usize,
base: usize,
first_half: bool,
extra_clock: bool,
}

impl LengthCounter {
Expand All @@ -18,7 +18,7 @@ impl LengthCounter {
active: false,
length: 0,
base,
first_half: false,
extra_clock: false,
}
}

Expand All @@ -41,7 +41,7 @@ impl LengthCounter {
if enable {
// Disabled -> enabled in the first half
// should clock once on enable
if !self.enable && self.first_half {
if !self.enable && self.extra_clock {
// Clock unless length reaches zero
if self.length != 0 {
self.clock();
Expand All @@ -64,7 +64,7 @@ impl LengthCounter {

// Reloading 0 -> max on trigger & enable in the first half
// should clock once on enable.
if self.enable && self.first_half {
if self.enable && self.extra_clock {
self.clock();
}
}
Expand All @@ -77,7 +77,7 @@ impl LengthCounter {
}

pub fn power_on(&mut self) {
self.first_half = false;
self.extra_clock = false;
}

pub fn power_off(&mut self) {
Expand All @@ -90,13 +90,10 @@ impl LengthCounter {
}

pub fn step(&mut self, frame: Frame) {
self.first_half = match frame.step {
0 | 2 | 4 | 6 => true,
1 | 3 | 5 | 7 => false,
_ => unreachable!(),
};
// Extra clock happens if next frame isn't active.
self.extra_clock = !can_clock(frame.next);

if frame.cycles != 0 || !self.first_half {
if !can_clock(frame.switched()) {
return;
}

Expand All @@ -119,3 +116,10 @@ impl LengthCounter {
self.length = self.length.saturating_sub(1);
}
}

fn can_clock(frame_index: Option<usize>) -> bool {
match frame_index {
Some(0) | Some(2) | Some(4) | Some(6) => true,
_ => false,
}
}
7 changes: 3 additions & 4 deletions core/src/apu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,10 @@ impl Apu {

/// Read PCM12 register
pub fn read_pcm12(&self) -> u8 {
let pcm = Pcm::default()
Pcm::default()
.with_low(self.tones[0].pcm())
.with_high(self.tones[1].pcm());

pcm.into_bits()
.with_high(self.tones[1].pcm())
.into_bits()
}

/// Read PCM34 register
Expand Down
10 changes: 8 additions & 2 deletions core/src/apu/tone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ impl Tone {
self.nr14 = Nr14::from_bits(0);
self.freq = Freq::from_bits(0);

self.index = 0;

self.length_counter.power_off();

self.dac.power_off();
Expand All @@ -260,11 +262,13 @@ impl Tone {
let times = self.divider.step(cycles);

for _ in 0..times {
self.update();
self.update_index();
}

self.write_amp();
}

fn update(&mut self) {
fn update_index(&mut self) {
if !self.is_active() {
return;
}
Expand All @@ -275,7 +279,9 @@ impl Tone {
self.reload_timer();

self.index = (self.index + 1) % 8;
}

fn write_amp(&mut self) {
let wave = match self.nr11.wave_duty() {
0 => [0, 0, 0, 0, 0, 0, 0, 1],
1 => [1, 0, 0, 0, 0, 0, 0, 1],
Expand Down
Loading

0 comments on commit 05f6400

Please sign in to comment.