feat(apu): add ChannelOutputs struct and channel_outputs() method

This commit is contained in:
2026-03-13 16:08:55 +03:00
parent 49568a582b
commit e63b5783bd
5 changed files with 86 additions and 3 deletions

View File

@@ -16,7 +16,7 @@ pub use nesemu_adapter_api as adapter_api;
#[cfg(feature = "adapter-headless")] #[cfg(feature = "adapter-headless")]
pub use nesemu_adapter_headless as adapter_headless; pub use nesemu_adapter_headless as adapter_headless;
pub use native_core::apu::{Apu, ApuStateTail}; pub use native_core::apu::{Apu, ApuStateTail, ChannelOutputs};
pub use native_core::bus::NativeBus; pub use native_core::bus::NativeBus;
pub use native_core::cpu::{Cpu6502, CpuBus, CpuError}; pub use native_core::cpu::{Cpu6502, CpuBus, CpuError};
pub use native_core::ines::{InesHeader, InesRom, Mirroring, parse_header, parse_rom}; pub use native_core::ines::{InesHeader, InesRom, Mirroring, parse_header, parse_rom};

View File

@@ -1,4 +1,4 @@
use super::types::{Apu, ApuStateTail}; use super::types::{Apu, ApuStateTail, ChannelOutputs};
impl Apu { impl Apu {
pub fn new() -> Self { pub fn new() -> Self {
@@ -287,4 +287,74 @@ impl Apu {
self.noise_timer_counter = state.noise_timer_counter; self.noise_timer_counter = state.noise_timer_counter;
self.noise_lfsr = if state.noise_lfsr == 0 { 1 } else { state.noise_lfsr }; self.noise_lfsr = if state.noise_lfsr == 0 { 1 } else { state.noise_lfsr };
} }
pub fn channel_outputs(&self) -> ChannelOutputs {
const PULSE_DUTY_TABLE: [[u8; 8]; 4] = [
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0],
[1, 0, 0, 1, 1, 1, 1, 1],
];
const TRIANGLE_SEQUENCE: [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,
];
let pulse1 = {
let duty = (self.io[0x00] >> 6) as usize;
let step = self.pulse_duty_step[0] as usize;
let volume = if (self.io[0x00] & 0x10) != 0 {
self.io[0x00] & 0x0F
} else {
self.envelope_decay[0]
};
let active = (self.channel_enable_mask & 0x01) != 0
&& self.length_counters[0] > 0
&& PULSE_DUTY_TABLE[duty][step] != 0
&& !self.sweep_mutes_channel(0, 0x02);
if active { volume } else { 0 }
};
let pulse2 = {
let duty = (self.io[0x04] >> 6) as usize;
let step = self.pulse_duty_step[1] as usize;
let volume = if (self.io[0x04] & 0x10) != 0 {
self.io[0x04] & 0x0F
} else {
self.envelope_decay[1]
};
let active = (self.channel_enable_mask & 0x02) != 0
&& self.length_counters[1] > 0
&& PULSE_DUTY_TABLE[duty][step] != 0
&& !self.sweep_mutes_channel(1, 0x06);
if active { volume } else { 0 }
};
let triangle = {
let active = (self.channel_enable_mask & 0x04) != 0
&& self.length_counters[2] > 0
&& self.triangle_linear_counter > 0;
if active {
TRIANGLE_SEQUENCE[self.triangle_step as usize & 0x1F]
} else {
0
}
};
let noise = {
let volume = if (self.io[0x0C] & 0x10) != 0 {
self.io[0x0C] & 0x0F
} else {
self.envelope_decay[2]
};
let active = (self.channel_enable_mask & 0x08) != 0
&& self.length_counters[3] > 0
&& (self.noise_lfsr & 1) == 0;
if active { volume } else { 0 }
};
let dmc = self.dmc_output_level;
ChannelOutputs { pulse1, pulse2, triangle, noise, dmc }
}
} }

View File

@@ -2,4 +2,4 @@ mod api;
mod timing; mod timing;
mod types; mod types;
pub use types::{Apu, ApuStateTail}; pub use types::{Apu, ApuStateTail, ChannelOutputs};

View File

@@ -1,3 +1,12 @@
#[derive(Debug, Clone, Copy, Default)]
pub struct ChannelOutputs {
pub pulse1: u8,
pub pulse2: u8,
pub triangle: u8,
pub noise: u8,
pub dmc: u8,
}
pub(super) const APU_FRAME_SEQ_4_STEP_CYCLES: u32 = 14_915; pub(super) const APU_FRAME_SEQ_4_STEP_CYCLES: u32 = 14_915;
pub(super) const APU_FRAME_SEQ_5_STEP_CYCLES: u32 = 18_641; pub(super) const APU_FRAME_SEQ_5_STEP_CYCLES: u32 = 18_641;
pub(super) const APU_QUARTER_FRAME_1: u32 = 3_729; pub(super) const APU_QUARTER_FRAME_1: u32 = 3_729;

View File

@@ -58,6 +58,10 @@ impl NativeBus {
self.apu.registers() self.apu.registers()
} }
pub fn apu_channel_outputs(&self) -> crate::native_core::apu::ChannelOutputs {
self.apu.channel_outputs()
}
pub fn render_frame(&self, out_rgba: &mut [u8], frame_number: u32, buttons: [bool; 8]) { pub fn render_frame(&self, out_rgba: &mut [u8], frame_number: u32, buttons: [bool; 8]) {
let _ = (frame_number, buttons); let _ = (frame_number, buttons);
let src = self.ppu.frame_buffer(); let src = self.ppu.frame_buffer();