Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 9s
751 lines
20 KiB
Rust
751 lines
20 KiB
Rust
macro_rules! lut {
|
|
($name:ident: [$ty:ty; $len:expr] = |$n:ident| $expr:expr) => {
|
|
const $name: [$ty; $len] = {
|
|
let mut table = [0; $len];
|
|
let mut $n = 0;
|
|
while $n < $len {
|
|
table[$n] = $expr;
|
|
$n += 1;
|
|
}
|
|
table
|
|
};
|
|
};
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub struct LengthTimerHigh(u8);
|
|
impl Debug;
|
|
length, set_length: 7, 3;
|
|
u16;
|
|
timer_high, set_timer_high: 2, 0;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct LengthCounter {
|
|
current: u16,
|
|
silenced: bool,
|
|
}
|
|
|
|
impl LengthCounter {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
current: 0,
|
|
silenced: true,
|
|
}
|
|
}
|
|
pub fn clock(&mut self) {
|
|
if self.current == 0 {
|
|
self.silenced = true;
|
|
} else {
|
|
self.current -= 1;
|
|
}
|
|
}
|
|
|
|
pub fn write(&mut self, length: u8) {
|
|
// self.reg.0 = val;
|
|
self.silenced = false;
|
|
const LENGTH_LUT: [u16; 0x20] = [
|
|
10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20,
|
|
96, 22, 192, 24, 72, 26, 16, 28, 32, 3,
|
|
];
|
|
self.current = LENGTH_LUT[length as usize] + 1; // I think?
|
|
}
|
|
|
|
pub fn silenced(&self) -> bool {
|
|
self.silenced
|
|
}
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub struct DutyVol(u8);
|
|
impl Debug;
|
|
duty, set_duty: 7, 6;
|
|
length_counter_halt, set_length_counter_halt: 5;
|
|
const_vol, set_const_vol: 4;
|
|
volume, set_volume: 3, 0;
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub struct Sweep(u8);
|
|
impl Debug;
|
|
enable, set_enable: 7;
|
|
period, set_period: 6, 4;
|
|
negate, set_negate: 3;
|
|
shift, set_shift: 2, 0;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct PulseChannel {
|
|
enabled: bool,
|
|
duty_vol: DutyVol,
|
|
|
|
sweep: Sweep,
|
|
sweep_reload: bool,
|
|
sweep_counter: u8,
|
|
|
|
counter: LengthCounter,
|
|
|
|
period: u16,
|
|
period_timer: u16,
|
|
cur: u8,
|
|
sample: u8,
|
|
|
|
envelope_start: bool,
|
|
envelope_counter: u8,
|
|
envelope_divider: u8,
|
|
extra: u16,
|
|
}
|
|
|
|
impl PulseChannel {
|
|
pub fn new(extra: u16) -> Self {
|
|
Self {
|
|
enabled: false,
|
|
duty_vol: DutyVol(0),
|
|
sweep: Sweep(0),
|
|
sweep_reload: false,
|
|
sweep_counter: 0,
|
|
counter: LengthCounter::new(),
|
|
period: 0,
|
|
period_timer: 0,
|
|
cur: 0,
|
|
sample: 0,
|
|
envelope_start: false,
|
|
envelope_counter: 0,
|
|
envelope_divider: 0,
|
|
extra,
|
|
}
|
|
}
|
|
|
|
pub fn write(&mut self, offset: u16, val: u8) {
|
|
match offset {
|
|
0x00 => {
|
|
self.duty_vol.0 = val;
|
|
self.envelope_start = true;
|
|
}
|
|
0x01 => {
|
|
self.sweep.0 = val;
|
|
self.sweep_reload = true;
|
|
}
|
|
0x02 => self.period = self.period & 0x700 | (val as u16),
|
|
0x03 => {
|
|
let reg = LengthTimerHigh(val);
|
|
self.counter.write(reg.length());
|
|
self.period = (self.period & 0xFF) | (reg.timer_high() << 8);
|
|
self.period_timer = self.period;
|
|
self.cur = 0;
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn volume(&self) -> u8 {
|
|
if self.duty_vol.const_vol() {
|
|
self.duty_vol.volume()
|
|
} else {
|
|
self.envelope_counter
|
|
}
|
|
}
|
|
|
|
pub fn clock(&mut self) {
|
|
if !self.enabled {
|
|
self.sample = 0;
|
|
return;
|
|
}
|
|
if self.period_timer == 0 {
|
|
self.period_timer = self.period;
|
|
self.cur = (self.cur + 1) % 8;
|
|
const DUTY: [[bool; 8]; 4] = [
|
|
[false, true, false, false, false, false, false, false],
|
|
[false, true, true, false, false, false, false, false],
|
|
[false, true, true, true, true, false, false, false],
|
|
[true, false, false, true, true, true, true, true],
|
|
];
|
|
self.sample = if DUTY[self.duty_vol.duty() as usize][self.cur as usize] {
|
|
self.volume()
|
|
} else {
|
|
0x00
|
|
};
|
|
} else {
|
|
self.period_timer -= 1;
|
|
}
|
|
// TODO
|
|
}
|
|
pub fn cur_sample(&self) -> u8 {
|
|
if self.enabled && !self.counter.silenced() && !self.sweep_silenced() {
|
|
self.sample
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
pub fn q_frame_clock(&mut self) {
|
|
if !self.enabled {
|
|
return;
|
|
}
|
|
if !self.duty_vol.length_counter_halt() {
|
|
self.counter.clock();
|
|
} else {
|
|
self.counter.silenced = false;
|
|
}
|
|
if !self.duty_vol.const_vol() {
|
|
if self.envelope_start || self.envelope_counter == 0 {
|
|
self.envelope_counter = 0xF;
|
|
self.envelope_divider = self.duty_vol.volume();
|
|
self.envelope_start = false;
|
|
} else if self.envelope_divider == 0 {
|
|
self.envelope_divider = self.duty_vol.volume();
|
|
self.envelope_counter -= 1;
|
|
} else {
|
|
self.envelope_divider -= 1;
|
|
}
|
|
}
|
|
}
|
|
fn target_period(&self) -> u16 {
|
|
let amt = self.period >> self.sweep.shift();
|
|
if self.sweep.negate() {
|
|
self.period.saturating_sub(amt + self.extra)
|
|
} else {
|
|
self.period + amt
|
|
}
|
|
}
|
|
fn sweep_silenced(&self) -> bool {
|
|
self.period < 8 || self.target_period() > 0x7FF
|
|
}
|
|
pub fn h_frame_clock(&mut self) {
|
|
self.q_frame_clock();
|
|
if self.sweep.enable() {
|
|
if self.sweep_reload {
|
|
self.sweep_counter = self.sweep.period();
|
|
self.sweep_reload = false;
|
|
} else if self.sweep_counter == 0 {
|
|
self.sweep_counter = self.sweep.period();
|
|
if self.period < 8 || self.target_period() < 0x7FF {
|
|
self.period = self.target_period();
|
|
}
|
|
} else {
|
|
self.sweep_counter -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn view(&self) -> String {
|
|
format!(
|
|
"Square Channel
|
|
Evelope Volume: {0:>4} ${0:02X}
|
|
Constant Volume: {1}
|
|
Length Counter - Halted: {2}
|
|
Duty: {3:>4} ${3:02X}
|
|
Sweep - Shift: {4:>4} ${4:02X}
|
|
Sweep - Negate: {5}
|
|
Sweep - Period: {6:>4} ${6:02X}
|
|
Sweep - Enabled: {7}
|
|
Period: {8:>4} ${8:04X}
|
|
Length Counter - Reload Value: {9:>4} ${9:04X}
|
|
Enabled: {10}
|
|
Timer: {11:>4} ${11:04X}
|
|
Duty Position: {12:>4} ${12:02X}
|
|
Length Counter - Counter: {13:>4} ${13:02X}
|
|
Envelope - Counter: {14:>4} ${14:02X}
|
|
Envelope - Divider: {15:>4} ${15:02X}
|
|
Output: {16:>4} ${16:02X}
|
|
",
|
|
self.duty_vol.volume(),
|
|
self.duty_vol.const_vol(),
|
|
self.duty_vol.length_counter_halt(),
|
|
self.duty_vol.duty(),
|
|
self.sweep.shift(),
|
|
self.sweep.negate(),
|
|
self.sweep.period(),
|
|
self.sweep.enable(),
|
|
self.period,
|
|
self.counter.current,
|
|
self.enabled,
|
|
self.period_timer,
|
|
self.cur, // ?
|
|
self.counter.current,
|
|
self.envelope_counter,
|
|
self.envelope_divider,
|
|
self.cur_sample(),
|
|
)
|
|
}
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct LengthCounterReg(u8);
|
|
impl Debug;
|
|
halt, set_halt: 7;
|
|
value, set_value: 6, 0;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct TriangleChannel {
|
|
enabled: bool,
|
|
length: LengthCounterReg,
|
|
reload: bool,
|
|
counter: LengthCounter,
|
|
|
|
length_counter: u16,
|
|
cur: u8,
|
|
period: u16,
|
|
period_timer: u16,
|
|
sample: u8,
|
|
}
|
|
|
|
impl TriangleChannel {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
length: LengthCounterReg(0),
|
|
counter: LengthCounter::new(),
|
|
reload: false,
|
|
enabled: false,
|
|
sample: 0,
|
|
period: 0,
|
|
period_timer: 0,
|
|
cur: 0,
|
|
length_counter: 0,
|
|
}
|
|
}
|
|
|
|
pub fn write(&mut self, offset: u16, val: u8) {
|
|
match offset {
|
|
0x00 => {
|
|
self.length.0 = val;
|
|
}
|
|
0x01 => (),
|
|
0x02 => self.period = self.period & 0x700 | (val as u16),
|
|
0x03 => {
|
|
// self.length_load.0 = val;
|
|
// self.reload = true;
|
|
let reg = LengthTimerHigh(val);
|
|
self.counter.write(reg.length());
|
|
self.period = (self.period & 0xFF) | (reg.timer_high() << 8);
|
|
self.period_timer = self.period;
|
|
self.cur = 0;
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn cur_sample(&self) -> u8 {
|
|
self.sample
|
|
}
|
|
|
|
pub fn clock(&mut self) {
|
|
if self.length_counter > 0 && self.period > 0 {
|
|
if self.period_timer == 0 {
|
|
self.period_timer = self.period;
|
|
const SAMPLES: [u8; 32] = [
|
|
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7,
|
|
8, 9, 10, 11, 12, 13, 14, 15,
|
|
];
|
|
self.cur = (self.cur + 1) % SAMPLES.len() as u8;
|
|
self.sample = SAMPLES[self.cur as usize];
|
|
} else {
|
|
self.period_timer -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn q_frame_clock(&mut self) {
|
|
if self.reload {
|
|
self.length_counter = self.length.value() as u16;
|
|
self.reload = self.length.halt();
|
|
} else if self.length_counter == 0 {
|
|
} else {
|
|
self.length_counter -= 1;
|
|
}
|
|
}
|
|
|
|
pub fn h_frame_clock(&mut self) {
|
|
self.q_frame_clock();
|
|
}
|
|
|
|
pub fn view(&self) -> String {
|
|
format!(
|
|
"Triangle Channel
|
|
Linear Counter - Reload: {0:>3} ${0:02X}
|
|
Linear Counter - Halted: {1}
|
|
Period: {2:>3} ${2:04X}
|
|
Length Counter - Reload Value: {3:>3} ${3:04X}
|
|
Enabled: {4}
|
|
Timer: {5:>3} ${5:02X}
|
|
Frequency: ???
|
|
Sequence Position: {6:>3} ${6:02X}
|
|
Length Counter - Counter: {7:>3} ${7:02X}
|
|
Linear Counter - Counter: {8:>3} ${8:02X}
|
|
Linear Counter - Reload Flag: {9}
|
|
Output: {10:>3} ${10:02X}
|
|
",
|
|
self.length.value(),
|
|
self.length.halt(),
|
|
self.period,
|
|
0,
|
|
self.enabled,
|
|
self.period_timer,
|
|
self.cur,
|
|
0,
|
|
self.length_counter,
|
|
self.reload,
|
|
self.cur_sample(),
|
|
)
|
|
}
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct NoiseEnvelope(u8);
|
|
impl Debug;
|
|
length_counter_halt, set_length_counter_halt: 5;
|
|
constant_volume, set_constant_volume: 4;
|
|
volume, set_volume: 3, 0;
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct ModePeriod(u8);
|
|
impl Debug;
|
|
mode, set_mode: 7;
|
|
period, set_period: 3, 0;
|
|
}
|
|
|
|
bitfield::bitfield! {
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct LengthCounterLoad(u8);
|
|
impl Debug;
|
|
length, set_length: 7, 3;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
#[allow(dead_code)]
|
|
struct NoiseChannel {
|
|
enabled: bool,
|
|
evelope: NoiseEnvelope,
|
|
mode_period: ModePeriod,
|
|
length_counter_load: LengthCounterLoad,
|
|
|
|
shr: u16,
|
|
count: u16,
|
|
length_counter: u8,
|
|
}
|
|
|
|
impl NoiseChannel {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
enabled: false,
|
|
evelope: NoiseEnvelope(0),
|
|
mode_period: ModePeriod(0),
|
|
length_counter_load: LengthCounterLoad(0),
|
|
shr: 1,
|
|
count: 0,
|
|
length_counter: 0,
|
|
}
|
|
}
|
|
|
|
pub fn write(&mut self, offset: u16, _val: u8) {
|
|
match offset {
|
|
0x00 => (),
|
|
0x01 => (),
|
|
0x02 => (),
|
|
0x03 => (),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn cur_sample(&self) -> u8 {
|
|
if self.length_counter == 0 || self.shr & 1 == 1 {
|
|
0
|
|
} else {
|
|
self.evelope.volume()
|
|
}
|
|
}
|
|
pub fn clock(&mut self) {
|
|
const PERIODS: &[u16] = &[
|
|
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4096,
|
|
];
|
|
if self.count == PERIODS[self.mode_period.period() as usize] / 2 {
|
|
self.count = 0;
|
|
let bit_0 = (self.shr & 0b1) << 14;
|
|
let other = if self.mode_period.mode() {
|
|
(self.shr & 0b100_0000) << 8
|
|
} else {
|
|
(self.shr & 0b10) << 13
|
|
};
|
|
self.shr = (self.shr >> 1) | (bit_0 ^ other);
|
|
} else {
|
|
self.count += 1;
|
|
}
|
|
}
|
|
pub fn q_frame_clock(&mut self) {}
|
|
pub fn h_frame_clock(&mut self) {
|
|
self.q_frame_clock();
|
|
}
|
|
|
|
pub fn view(&self) -> String {
|
|
format!("")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct DeltaChannel {
|
|
enabled: bool,
|
|
sample: u8,
|
|
}
|
|
|
|
impl DeltaChannel {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
enabled: false,
|
|
sample: 0,
|
|
}
|
|
}
|
|
|
|
pub fn write(&mut self, offset: u16, _val: u8) {
|
|
match offset {
|
|
0x00 => (),
|
|
0x01 => (),
|
|
0x02 => (),
|
|
0x03 => (),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn cur_sample(&self) -> u8 {
|
|
self.sample
|
|
}
|
|
pub fn clock(&mut self) {}
|
|
pub fn q_frame_clock(&mut self) {}
|
|
pub fn h_frame_clock(&mut self) {}
|
|
|
|
pub fn int(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
pub fn view(&self) -> String {
|
|
format!("")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct FrameCounter {
|
|
count: usize,
|
|
mode_5_step: bool,
|
|
interrupt_enabled: bool,
|
|
irq: bool,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct APU {
|
|
pulse_1: PulseChannel,
|
|
pulse_2: PulseChannel,
|
|
triangle: TriangleChannel,
|
|
noise: NoiseChannel,
|
|
dmc: DeltaChannel,
|
|
frame_counter: FrameCounter,
|
|
|
|
samples: Vec<u8>,
|
|
}
|
|
|
|
impl std::fmt::Debug for APU {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"APU
|
|
{:#?}
|
|
{:#?}
|
|
{:#?}
|
|
{:#?}
|
|
{:#?}
|
|
",
|
|
self.pulse_1,
|
|
self.pulse_2,
|
|
self.triangle,
|
|
self.noise,
|
|
self.dmc,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl APU {
|
|
pub fn init() -> Self {
|
|
Self {
|
|
pulse_1: PulseChannel::new(1),
|
|
pulse_2: PulseChannel::new(0),
|
|
triangle: TriangleChannel::new(),
|
|
noise: NoiseChannel::new(),
|
|
dmc: DeltaChannel::new(),
|
|
frame_counter: FrameCounter {
|
|
mode_5_step: false,
|
|
interrupt_enabled: true,
|
|
count: 0,
|
|
irq: false,
|
|
},
|
|
|
|
samples: Vec::with_capacity(10000),
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
*self = Self::init();
|
|
}
|
|
|
|
pub fn read_reg(&mut self, offset: u16) -> u8 {
|
|
// println!("APU read: {offset:02X}");
|
|
match offset {
|
|
0x15 => {
|
|
let val = (u8::from(self.dmc.int()) << 7)
|
|
| (u8::from(self.frame_counter.irq) << 6)
|
|
| (u8::from(self.dmc.enabled) << 4)
|
|
| (u8::from(self.noise.enabled) << 3)
|
|
| (u8::from(self.triangle.enabled) << 2)
|
|
| (u8::from(self.pulse_2.enabled) << 1)
|
|
| (u8::from(self.pulse_1.enabled) << 0);
|
|
self.frame_counter.irq = false;
|
|
val
|
|
}
|
|
_ => panic!("No register at {:X}", offset),
|
|
}
|
|
}
|
|
|
|
pub fn write_reg(&mut self, offset: u16, val: u8) {
|
|
// println!("APU write: {offset:02X} <= {val:02X}");
|
|
match offset {
|
|
0x00..0x04 => self.pulse_1.write(offset - 0x00, val),
|
|
0x04..0x08 => self.pulse_2.write(offset - 0x04, val),
|
|
0x08..0x0C => self.triangle.write(offset - 0x08, val),
|
|
0x0C..0x10 => self.noise.write(offset - 0x0C, val),
|
|
0x10..0x14 => self.dmc.write(offset - 0x10, val),
|
|
0x14 => (),
|
|
0x15 => {
|
|
self.dmc.enabled = val & 0b0001_0000 != 0;
|
|
self.noise.enabled = val & 0b0000_1000 != 0;
|
|
self.triangle.enabled = val & 0b0000_0100 != 0;
|
|
self.pulse_2.enabled = val & 0b0000_0010 != 0;
|
|
self.pulse_1.enabled = val & 0b0000_0001 != 0;
|
|
}
|
|
0x17 => {
|
|
self.frame_counter.mode_5_step = val & 0b1000_0000 != 0;
|
|
self.frame_counter.interrupt_enabled = val & 0b0100_0000 == 0;
|
|
if !self.frame_counter.interrupt_enabled {
|
|
self.frame_counter.irq = false;
|
|
}
|
|
}
|
|
// _ => (),
|
|
_ => panic!("No register at {:X}", offset),
|
|
}
|
|
}
|
|
|
|
fn q_frame_clock(&mut self) {
|
|
self.pulse_1.q_frame_clock();
|
|
self.pulse_2.q_frame_clock();
|
|
self.triangle.q_frame_clock();
|
|
self.noise.q_frame_clock();
|
|
self.dmc.q_frame_clock();
|
|
}
|
|
|
|
fn h_frame_clock(&mut self) {
|
|
self.pulse_1.h_frame_clock();
|
|
self.pulse_2.h_frame_clock();
|
|
self.triangle.h_frame_clock();
|
|
self.noise.h_frame_clock();
|
|
self.dmc.h_frame_clock();
|
|
}
|
|
|
|
fn gen_sample(&mut self) {
|
|
lut!(P_LUT: [u8; 32] = |n| (95.52 / (8128.0 / n as f64 + 100.0) * 255.0) as u8);
|
|
let pulse_out = P_LUT[(self.pulse_1.cur_sample() + self.pulse_2.cur_sample()) as usize];
|
|
lut!(TND_LUT: [u8; 204] = |n| (163.67 / (24329.0 / n as f64 + 100.0) * 255.0) as u8);
|
|
let tnd_out = TND_LUT[3 * self.triangle.cur_sample() as usize
|
|
+ 2 * self.noise.cur_sample() as usize
|
|
+ self.dmc.cur_sample() as usize];
|
|
self.samples.push(pulse_out + tnd_out);
|
|
}
|
|
|
|
pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool {
|
|
if ppu_cycle % 6 == 1 {
|
|
// APU Frame Counter clock cycle
|
|
self.frame_counter.count += 1;
|
|
if self.frame_counter.count == 3728 {
|
|
self.q_frame_clock();
|
|
} else if self.frame_counter.count == 7456 {
|
|
self.h_frame_clock();
|
|
} else if self.frame_counter.count == 11185 {
|
|
self.q_frame_clock();
|
|
} else if self.frame_counter.count == 14914 && !self.frame_counter.mode_5_step {
|
|
self.frame_counter.irq = self.frame_counter.interrupt_enabled;
|
|
self.h_frame_clock();
|
|
} else if self.frame_counter.count == 14915 && !self.frame_counter.mode_5_step {
|
|
self.frame_counter.count = 0;
|
|
self.frame_counter.irq = self.frame_counter.interrupt_enabled;
|
|
} else if self.frame_counter.count == 18640 {
|
|
self.h_frame_clock();
|
|
self.frame_counter.count = 0;
|
|
}
|
|
|
|
self.pulse_1.clock();
|
|
self.pulse_2.clock();
|
|
self.noise.clock();
|
|
self.dmc.clock();
|
|
}
|
|
if ppu_cycle % 3 == 1 {
|
|
self.triangle.clock();
|
|
}
|
|
if ppu_cycle % (6 * 4) == 1 {
|
|
self.gen_sample();
|
|
}
|
|
false
|
|
}
|
|
|
|
pub fn get_frame_samples(&self) -> &[u8] {
|
|
// println!("'Frame' of samples: {}", self.samples.len());
|
|
&self.samples
|
|
}
|
|
|
|
pub fn reset_frame_samples(&mut self) {
|
|
self.samples.clear();
|
|
}
|
|
|
|
pub fn peek_nmi(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
pub fn nmi_waiting(&mut self) -> bool {
|
|
false
|
|
}
|
|
|
|
pub fn peek_irq(&self) -> bool {
|
|
self.frame_counter.irq
|
|
}
|
|
|
|
pub fn irq_waiting(&mut self) -> bool {
|
|
self.frame_counter.irq
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "iced")]
|
|
mod apu_iced {
|
|
use super::*;
|
|
use iced::{
|
|
Element, Font,
|
|
widget::{column, text},
|
|
};
|
|
|
|
impl APU {
|
|
pub fn view<'s, T: 's>(&'s self) -> Element<'s, T> {
|
|
column![
|
|
text(self.pulse_1.view()).font(Font::MONOSPACE),
|
|
text(self.pulse_2.view()).font(Font::MONOSPACE),
|
|
text(self.triangle.view()).font(Font::MONOSPACE),
|
|
text(self.noise.view()).font(Font::MONOSPACE),
|
|
text(self.dmc.view()).font(Font::MONOSPACE),
|
|
]
|
|
.into()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|