Split WASM and native versions, and move iced support code to native

This commit is contained in:
2026-03-27 00:27:34 -05:00
parent 3010469c8a
commit b433148843
23 changed files with 2330 additions and 1012 deletions

View File

@@ -1,9 +1,5 @@
use std::iter::repeat_n;
use iced::{
Element, Font,
widget::{column, text},
};
use tracing::debug;
macro_rules! lut {
@@ -89,8 +85,10 @@ bitfield::bitfield! {
struct PulseChannel {
enabled: bool,
duty_vol: DutyVol,
sweep: Sweep,
sweep_reload: bool,
sweep_counter: u8,
counter: LengthCounter,
@@ -102,15 +100,17 @@ struct PulseChannel {
envelope_start: bool,
envelope_counter: u8,
envelope_divider: u8,
extra: u16,
}
impl PulseChannel {
pub fn new() -> Self {
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,
@@ -119,6 +119,7 @@ impl PulseChannel {
envelope_start: false,
envelope_counter: 0,
envelope_divider: 0,
extra,
}
}
@@ -131,7 +132,7 @@ impl PulseChannel {
0x01 => {
self.sweep.0 = val;
self.sweep_reload = true;
},
}
0x02 => self.period = self.period & 0x700 | (val as u16),
0x03 => {
let reg = LengthTimerHigh(val);
@@ -143,7 +144,7 @@ impl PulseChannel {
_ => unreachable!(),
}
}
fn volume(&self) -> u8 {
if self.duty_vol.const_vol() {
self.duty_vol.volume()
@@ -151,7 +152,7 @@ impl PulseChannel {
self.envelope_counter
}
}
pub fn clock(&mut self) {
if !self.enabled {
self.sample = 0;
@@ -177,7 +178,7 @@ impl PulseChannel {
// TODO
}
pub fn cur_sample(&self) -> u8 {
if self.enabled && !self.counter.silenced() {
if self.enabled && !self.counter.silenced() && !self.sweep_silenced() {
self.sample
} else {
0
@@ -205,30 +206,54 @@ impl PulseChannel {
}
}
}
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<T>(&self) -> Element<'_, T> {
text!(
pub fn view(&self) -> String {
format!(
"Square Channel
Evelope Volume: {0:>3} ${0:02X}
Evelope Volume: {0:>4} ${0:02X}
Constant Volume: {1}
Length Counter - Halted: {2}
Duty: {3:>3} ${3:02X}
Sweep - Shift: {4:>3} ${4:02X}
Duty: {3:>4} ${3:02X}
Sweep - Shift: {4:>4} ${4:02X}
Sweep - Negate: {5}
Sweep - Period: {6:>3} ${6:02X}
Sweep - Period: {6:>4} ${6:02X}
Sweep - Enabled: {7}
Period: {8:>3} ${8:04X}
Length Counter - Reload Value: {9:>3} ${9:04X}
Period: {8:>4} ${8:04X}
Length Counter - Reload Value: {9:>4} ${9:04X}
Enabled: {10}
Timer: {11:>3} ${11:04X}
Duty Position: {12:>3} ${12:02X}
Length Counter - Counter: {13:>3} ${13:02X}
Envelope - Counter: {14:>3} ${14:02X}
Envelope - Divider: {15:>3} ${15:02X}
Output: {16:>3} ${16:02X}
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(),
@@ -242,14 +267,12 @@ Output: {16:>3} ${16:02X}
0,
self.enabled,
self.period_timer,
self.cur, // ?
self.cur, // ?
self.counter.current,
self.envelope_counter,
self.envelope_divider,
self.cur_sample(),
)
.font(Font::MONOSPACE)
.into()
}
}
@@ -344,8 +367,8 @@ impl TriangleChannel {
self.q_frame_clock();
}
pub fn view<T>(&self) -> Element<'_, T> {
text!(
pub fn view(&self) -> String {
format!(
"Triangle Channel
Linear Counter - Reload: {0:>3} ${0:02X}
Linear Counter - Halted: {1}
@@ -372,8 +395,6 @@ Output: {10:>3} ${10:02X}
self.reload,
self.cur_sample(),
)
.font(Font::MONOSPACE)
.into()
}
}
@@ -465,8 +486,8 @@ impl NoiseChannel {
self.q_frame_clock();
}
pub fn view<T>(&self) -> Element<'_, T> {
text!("").font(Font::MONOSPACE).into()
pub fn view(&self) -> String {
format!("")
}
}
@@ -505,8 +526,8 @@ impl DeltaChannel {
false
}
pub fn view<T>(&self) -> Element<'_, T> {
text!("").font(Font::MONOSPACE).into()
pub fn view(&self) -> String {
format!("")
}
}
@@ -544,8 +565,8 @@ impl std::fmt::Debug for APU {
impl APU {
pub fn init() -> Self {
Self {
pulse_1: PulseChannel::new(),
pulse_2: PulseChannel::new(),
pulse_1: PulseChannel::new(1),
pulse_2: PulseChannel::new(0),
triangle: TriangleChannel::new(),
noise: NoiseChannel::new(),
dmc: DeltaChannel::new(),
@@ -695,15 +716,26 @@ impl APU {
pub fn irq_waiting(&mut self) -> bool {
self.frame_counter.irq
}
}
pub fn view<'s, T: 's>(&'s self) -> Element<'s, T> {
column![
self.pulse_1.view(),
self.pulse_2.view(),
self.triangle.view(),
self.noise.view(),
self.dmc.view(),
]
.into()
#[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()
}
}
}