Minor improvements to APU
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s

This commit is contained in:
2026-04-08 21:16:54 -05:00
parent 875e493cd2
commit f84009aeb9
4 changed files with 156 additions and 14 deletions

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@
*.nes *.nes
*.zip *.zip
*.dmp *.dmp
wasm/
!wasm/wasm.html

View File

@@ -1,7 +1,3 @@
use std::iter::repeat_n;
use tracing::debug;
macro_rules! lut { macro_rules! lut {
($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => { ($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => {
const $name: [$ty; $len] = { const $name: [$ty; $len] = {
@@ -264,7 +260,7 @@ Output: {16:>4} ${16:02X}
self.sweep.period(), self.sweep.period(),
self.sweep.enable(), self.sweep.enable(),
self.period, self.period,
0, self.counter.current,
self.enabled, self.enabled,
self.period_timer, self.period_timer,
self.cur, // ? self.cur, // ?
@@ -423,6 +419,7 @@ bitfield::bitfield! {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)]
struct NoiseChannel { struct NoiseChannel {
enabled: bool, enabled: bool,
evelope: NoiseEnvelope, evelope: NoiseEnvelope,
@@ -447,7 +444,7 @@ impl NoiseChannel {
} }
} }
pub fn write(&mut self, offset: u16, val: u8) { pub fn write(&mut self, offset: u16, _val: u8) {
match offset { match offset {
0x00 => (), 0x00 => (),
0x01 => (), 0x01 => (),
@@ -505,7 +502,7 @@ impl DeltaChannel {
} }
} }
pub fn write(&mut self, offset: u16, val: u8) { pub fn write(&mut self, offset: u16, _val: u8) {
match offset { match offset {
0x00 => (), 0x00 => (),
0x01 => (), 0x01 => (),
@@ -553,12 +550,21 @@ pub struct APU {
impl std::fmt::Debug for APU { impl std::fmt::Debug for APU {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!( write!(
// f, f,
// "PPU: f {}, s {}, p {}", "APU
// self.frame_count, self.scanline, self.pixel {:#?}
// ) {:#?}
Ok(()) {:#?}
{:#?}
{:#?}
",
self.pulse_1,
self.pulse_2,
self.triangle,
self.noise,
self.dmc,
)
} }
} }
@@ -577,7 +583,7 @@ impl APU {
irq: false, irq: false,
}, },
samples: vec![0; 100], samples: Vec::with_capacity(10000),
} }
} }
@@ -739,3 +745,6 @@ mod apu_iced {
} }
} }
} }
#[cfg(test)]
mod tests;

98
src/apu/tests.rs Normal file
View File

@@ -0,0 +1,98 @@
use super::*;
#[track_caller]
fn run_ppu_cycles(apu: &mut APU, cycles: usize) {
for i in 0..cycles {
apu.run_one_clock_cycle(i);
}
}
trait Pattern {
fn matches(&mut self, val: u8) -> bool;
}
impl Pattern for u8 {
fn matches(&mut self, val: u8) -> bool {
val == *self
}
}
impl<I: Iterator<Item = u8>> Pattern for I {
fn matches(&mut self, val: u8) -> bool {
self.next().is_some_and(|v| v == val)
}
}
fn is(samples: &[u8], mut pattern: impl Pattern) -> bool {
for s in samples {
if !pattern.matches(*s) {
return false;
}
}
true
}
#[test]
fn check_is() {
assert!(&[0; 20], 0..5);
}
macro_rules! cycle_check {
(|$apu:ident| $init:expr; $([$pat:expr; $count:expr]),* ; [$final:expr; _]) => {
let mut $apu = APU::init();
$init;
run_ppu_cycles(&mut $apu, 10000);
let mut s = $apu.get_frame_samples();
let mut cur = 0;
$(
let count = $count;
if !is(&s[..count], $pat) {
panic!("Incorrect samples from {} to {}:\nExpected: {:?}\n Found: {:?}", cur, cur + count, $pat, &s[..count]);
}
s = &s[count..];
cur += count;
)*
if !is(s, $final) {
panic!("Incorrect samples from {} to {}:\nExpected: {:?}\n Found: {:?}", cur, cur + count, $final, s);
}
};
}
const SNDCHN: u16 = 0x15;
const PULSE_CH1_DLCV: u16 = 0x00;
const PULSE_CH1_SWEEP: u16 = 0x01;
const PULSE_CH1_TLOW: u16 = 0x02;
const PULSE_CH1_LCTH: u16 = 0x03;
#[test]
fn run_apu() {
let mut apu = APU::init();
run_ppu_cycles(&mut apu, 10000);
println!("Count: {}", apu.get_frame_samples().len());
assert_eq!(apu.get_frame_samples(), &[0; 417]);
}
#[test]
fn run_apu_pulse_1() {
// Base? case
cycle_check!(|apu| {
apu.write_reg(SNDCHN, 0x01);
apu.write_reg(PULSE_CH1_DLCV, 0xBF);
apu.write_reg(PULSE_CH1_SWEEP, 0x7F);
apu.write_reg(PULSE_CH1_TLOW, 0x6F);
apu.write_reg(PULSE_CH1_LCTH, 0xF0);
// 1666 APU cycles (every fourth is a sample)
// Advance in duty cycle every 111 + 1 apu cycle (28 samples)
}; [0; 28], [37; 28*4], [0; 28*4], [37; 28*4] ; [0; _]);
cycle_check!(|apu| {
apu.write_reg(SNDCHN, 0x01);
apu.write_reg(PULSE_CH1_DLCV, 0xBF);
apu.write_reg(PULSE_CH1_SWEEP, 0x7F);
apu.write_reg(PULSE_CH1_TLOW, 0x60);
apu.write_reg(PULSE_CH1_LCTH, 0xF0);
// 1666 APU cycles (every fourth is a sample)
// Advance in duty cycle every 96 + 1 apu cycle (24.25 samples)
}; [0; 24], [37; 24*4+1], [0; 24*4+1], [37; 24*4+1], [0; 24*4 + 1] ; [37; _]);
}

View File

@@ -0,0 +1,33 @@
.include "testing.s"
zp_res TIMER_LOW
zp_res SWEEP
zp_res DLCV
; zp_res TIMER_LOW
reset:
sei
cld
ldx #$FF
txs
lda #$01
sta SNDCHN
lda #$BF ; Duty 2, LC halted, evelope enabled, volume = F
sta PULSE_CH1_DLCV
sta DLCV
lda #$7F ;
sta PULSE_CH1_SWEEP
sta SWEEP
lda #$6F
sta PULSE_CH1_TLOW
sta TIMER_LOW
lda #$F0
sta PULSE_CH1_LCTH
brk
loop:
jmp loop
irq:
nmi:
rti