Minor improvements to APU
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
||||
*.nes
|
||||
*.zip
|
||||
*.dmp
|
||||
wasm/
|
||||
!wasm/wasm.html
|
||||
|
||||
37
src/apu.rs
37
src/apu.rs
@@ -1,7 +1,3 @@
|
||||
use std::iter::repeat_n;
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
macro_rules! lut {
|
||||
($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => {
|
||||
const $name: [$ty; $len] = {
|
||||
@@ -264,7 +260,7 @@ Output: {16:>4} ${16:02X}
|
||||
self.sweep.period(),
|
||||
self.sweep.enable(),
|
||||
self.period,
|
||||
0,
|
||||
self.counter.current,
|
||||
self.enabled,
|
||||
self.period_timer,
|
||||
self.cur, // ?
|
||||
@@ -423,6 +419,7 @@ bitfield::bitfield! {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct NoiseChannel {
|
||||
enabled: bool,
|
||||
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 {
|
||||
0x00 => (),
|
||||
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 {
|
||||
0x00 => (),
|
||||
0x01 => (),
|
||||
@@ -553,12 +550,21 @@ pub struct APU {
|
||||
|
||||
impl std::fmt::Debug for APU {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(
|
||||
// f,
|
||||
// "PPU: f {}, s {}, p {}",
|
||||
// self.frame_count, self.scanline, self.pixel
|
||||
// )
|
||||
Ok(())
|
||||
write!(
|
||||
f,
|
||||
"APU
|
||||
{:#?}
|
||||
{:#?}
|
||||
{:#?}
|
||||
{:#?}
|
||||
{:#?}
|
||||
",
|
||||
self.pulse_1,
|
||||
self.pulse_2,
|
||||
self.triangle,
|
||||
self.noise,
|
||||
self.dmc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,7 +583,7 @@ impl APU {
|
||||
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
98
src/apu/tests.rs
Normal 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; _]);
|
||||
}
|
||||
33
src/test_roms/apu_pulse_1_test.s
Normal file
33
src/test_roms/apu_pulse_1_test.s
Normal 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
|
||||
Reference in New Issue
Block a user