From 4218c68b2d079bfe64c641242606624d7dca5d2b Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Thu, 12 Jun 2025 13:42:33 +0200 Subject: [PATCH 01/10] added a basic apu struct implemented ports for input/output between s-cpu and spc700 --- aliusnes/src/apu/mod.rs | 109 +++++++++++++++++++++++++++++++++ aliusnes/src/bus/system_bus.rs | 23 ++----- 2 files changed, 114 insertions(+), 18 deletions(-) diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index 1394a15..590b15a 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -1 +1,110 @@ +use crate::apu::spc700::Spc700; +use crate::bus::{Access, Address, Bus}; + pub mod spc700; + +pub struct Apu { + spc700: Spc700, + bus: ApuBus, +} + +struct ApuBus { + aram: [u8; 0x10000], + apuio: [u8; 4], + cpuio: [u8; 4], +} + +impl Apu { + pub fn new() -> Apu { + Apu { + spc700: Spc700::new(), + bus: ApuBus { + aram: [0; 0x10000], + apuio: [0; 4], + cpuio: [0; 4], + }, + } + } + + pub fn step(&mut self) { + let Apu { + ref mut bus, + ref mut spc700, + } = self; + + spc700.step(bus); + } +} + +impl Access for Apu { + fn read(&mut self, addr: u16, _: u64) -> Option { + match addr { + 0x2140..=0x2143 => Some(self.bus.cpuio[addr as usize & 3]), + _ => None, + } + } + + fn write(&mut self, addr: u16, data: u8) { + if let 0x2140..=0x2143 = addr { + self.bus.apuio[addr as usize & 3] = data + } + } +} + +impl ApuBus { + fn read(&self, addr: u16) -> u8 { + match addr { + // write-only area + 0x00F0 | 0x00F1 | 0x00FA..=0x00FC => 0, + 0x00F2 => todo!(), + 0x00F3 => todo!(), + 0x00F4..=0x00F7 => self.apuio[addr as usize & 3], + 0x00FD => todo!(), + 0x00FE => todo!(), + 0x00FF => todo!(), + _ => self.aram[addr as usize], + } + } + + fn write(&mut self, addr: u16, data: u8) { + match addr { + 0x00F0 => todo!(), + 0x00F1 => todo!(), + 0x00F2 => todo!(), + 0x00F3 => todo!(), + 0x00F4..=0x00F7 => self.cpuio[addr as usize & 3] = data, + 0x00FA => todo!(), + 0x00FB => todo!(), + 0x00FC => todo!(), + // read-only area + 0x00FD..=0x00FF => (), + _ => self.aram[addr as usize] = data, + } + } +} + +impl Bus for ApuBus { + fn peek_at(&self, _addr: Address) -> Option { + todo!() + } + + fn read_and_tick(&mut self, addr: Address) -> u8 { + self.read(addr.offset) + } + + fn write_and_tick(&mut self, addr: Address, data: u8) { + self.write(addr.offset, data) + } + + fn add_io_cycles(&mut self, _cycles: usize) { + todo!() + } + + fn fired_nmi(&mut self) -> bool { + todo!() + } + + fn fired_irq(&mut self) -> bool { + todo!() + } +} diff --git a/aliusnes/src/bus/system_bus.rs b/aliusnes/src/bus/system_bus.rs index c8f80c9..653f2f5 100644 --- a/aliusnes/src/bus/system_bus.rs +++ b/aliusnes/src/bus/system_bus.rs @@ -1,3 +1,4 @@ +use crate::apu::Apu; use crate::bus::dma::Dma; use crate::bus::math::Math; use crate::bus::wram::Wram; @@ -10,13 +11,13 @@ use crate::utils::int_traits::ManipulateU16; pub struct SystemBus { mdr: u8, fast_rom_enabled: bool, + apu: Apu, cart: Cart, pub dma: Dma, math: Math, pub ppu: Ppu, pub scheduler: Scheduler, wram: Wram, - dummy_apu: [u8; 4], } impl SystemBus { @@ -24,13 +25,13 @@ impl SystemBus { Self { mdr: 0, fast_rom_enabled: false, + apu: Apu::new(), ppu: Ppu::new(cart.model), scheduler: Scheduler::new(), cart, dma: Dma::new(), math: Math::new(), wram: Wram::new(), - dummy_apu: [0xAA, 0, 0, 0], } } @@ -41,18 +42,7 @@ impl SystemBus { pub fn read_b(&mut self, addr: u16) -> u8 { if let Some(val) = match addr.low_byte() { 0x34..=0x3F => self.ppu.read(addr, self.scheduler.cycles), - 0x40..=0x43 => { - let ch = ((addr - 0x2140) % 4) as usize; - - let value = self.dummy_apu[ch]; - self.dummy_apu[ch] = match ch { - 0 => 0xAA, - 1 => 0xBB, - _ => 0, - }; - - Some(value) - }, + 0x40..=0x43 => self.apu.read(addr, 0), 0x80 => self.wram.read(addr, 0), _ => None, } { @@ -116,10 +106,7 @@ impl SystemBus { pub fn write_b(&mut self, addr: u16, data: u8) { match addr.low_byte() { 0x00..=0x33 => self.ppu.write(addr, data), - 0x40..=0x43 => { - let ch = ((addr - 0x2140) % 4) as usize; - self.dummy_apu[ch] = data; - }, + 0x40..=0x43 => self.apu.write(addr, data), 0x80..=0x83 => self.wram.write(addr, data), _ => println!("Tried to write at {addr:#0x} val: {data:#04x}"), } From f02b63e1f4b01190b5d8f8653c7dd7124dd5bb70 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:33:40 +0200 Subject: [PATCH 02/10] run the apu added an event to schedule it, implemented communication between main cpu and audio cpu --- .gitignore | 3 +- aliusnes/Cargo.toml | 2 + aliusnes/src/apu/mod.rs | 102 ++++++++++++++++++++++++--------- aliusnes/src/apu/spc700/cpu.rs | 2 +- aliusnes/src/bus/dma.rs | 2 +- aliusnes/src/bus/math.rs | 2 +- aliusnes/src/bus/mod.rs | 2 +- aliusnes/src/bus/system_bus.rs | 16 +++--- aliusnes/src/bus/wram.rs | 2 +- aliusnes/src/emu.rs | 3 + aliusnes/src/ppu.rs | 2 +- aliusnes/src/scheduler.rs | 2 + 12 files changed, 98 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 2ebc5ea..426df58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -/Cargo.lock \ No newline at end of file +/Cargo.lock +/aliusnes/src/apu/spc700/ipl_boot.rom diff --git a/aliusnes/Cargo.toml b/aliusnes/Cargo.toml index 95ee1da..550fa68 100644 --- a/aliusnes/Cargo.toml +++ b/aliusnes/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +default = ["apu"] +apu = [] log = ["simplelog"] trace = ["log"] diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index 590b15a..97d4db9 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -1,53 +1,88 @@ use crate::apu::spc700::Spc700; use crate::bus::{Access, Address, Bus}; +use crate::cart::info::Model; +use crate::scheduler::{Event, Scheduler}; pub mod spc700; +#[cfg(feature = "apu")] +static IPL_ROM: &[u8; 0x40] = include_bytes!("spc700/ipl_boot.rom"); + +#[cfg(not(feature = "apu"))] +static IPL_ROM: &[u8; 0x40] = &[0; 0x40]; + +const NTSC_MASTER_CLOCK: u128 = 21_477_270; +const PAL_MASTER_CLOCK: u128 = 21_281_370; + +const SPC700_CLOCK: u128 = 1_024_000; + +const APU_EVENT_PERIOD_NTSC: u64 = ((32 * NTSC_MASTER_CLOCK) / SPC700_CLOCK) as u64; +const APU_EVENT_PERIOD_PAL: u64 = ((32 * PAL_MASTER_CLOCK) / SPC700_CLOCK) as u64; + pub struct Apu { spc700: Spc700, bus: ApuBus, + model: Model, } struct ApuBus { aram: [u8; 0x10000], apuio: [u8; 4], cpuio: [u8; 4], + cycles: u64, } impl Apu { - pub fn new() -> Apu { + pub fn new(model: Model) -> Apu { Apu { spc700: Spc700::new(), bus: ApuBus { aram: [0; 0x10000], apuio: [0; 4], cpuio: [0; 4], + cycles: 0, }, + model, } } - pub fn step(&mut self) { + pub(crate) fn catch_up_to_master(&mut self, time: u64) { let Apu { ref mut bus, ref mut spc700, + ref model, } = self; - spc700.step(bus); + let target_time = match model { + Model::Ntsc => time as u128 * SPC700_CLOCK / NTSC_MASTER_CLOCK, + Model::Pal => time as u128 * SPC700_CLOCK / PAL_MASTER_CLOCK, + }; + + while bus.cycles < target_time as u64 { + spc700.step(bus); + } + } + + pub(crate) fn handle_event(&mut self, scheduler: &mut Scheduler, time: u64) { + self.catch_up_to_master(time); + + let period = match self.model { + Model::Ntsc => APU_EVENT_PERIOD_NTSC, + Model::Pal => APU_EVENT_PERIOD_PAL, + }; + scheduler.add_event(Event::Apu, time + period); } } impl Access for Apu { - fn read(&mut self, addr: u16, _: u64) -> Option { - match addr { - 0x2140..=0x2143 => Some(self.bus.cpuio[addr as usize & 3]), - _ => None, - } + fn read(&mut self, addr: u16, time: u64) -> Option { + self.catch_up_to_master(time); + Some(self.bus.cpuio[addr as usize & 3]) } - fn write(&mut self, addr: u16, data: u8) { - if let 0x2140..=0x2143 = addr { - self.bus.apuio[addr as usize & 3] = data - } + fn write(&mut self, addr: u16, data: u8, time: u64) { + self.catch_up_to_master(time); + self.bus.apuio[addr as usize & 3] = data; } } @@ -55,13 +90,21 @@ impl ApuBus { fn read(&self, addr: u16) -> u8 { match addr { // write-only area - 0x00F0 | 0x00F1 | 0x00FA..=0x00FC => 0, - 0x00F2 => todo!(), - 0x00F3 => todo!(), + 0x00F0 | 0x00F1 | 0x00FA..=0x00FC => { + println!("Attempted to read write-only registers, returning 0"); + 0 + }, + // dsp registers + 0x00F2 | 0x00F3 => { + println!("Attempted to read dsp registers, returning 0"); + 0 + }, 0x00F4..=0x00F7 => self.apuio[addr as usize & 3], - 0x00FD => todo!(), - 0x00FE => todo!(), - 0x00FF => todo!(), + // timer registers + 0x00FD..=0x00FF => { + println!("Attempted to read timer registers, returning 0"); + 0 + }, _ => self.aram[addr as usize], } } @@ -70,12 +113,15 @@ impl ApuBus { match addr { 0x00F0 => todo!(), 0x00F1 => todo!(), - 0x00F2 => todo!(), - 0x00F3 => todo!(), - 0x00F4..=0x00F7 => self.cpuio[addr as usize & 3] = data, - 0x00FA => todo!(), - 0x00FB => todo!(), - 0x00FC => todo!(), + // dsp registers + 0x00F2 | 0x00F3 => println!("Attempted to write dsp registers"), + 0x00F4..=0x00F7 => { + self.cpuio[addr as usize & 3] = data; + }, + // timer registers + 0x00FA..=0x00FC => { + println!("Attempted to write timer registers"); + }, // read-only area 0x00FD..=0x00FF => (), _ => self.aram[addr as usize] = data, @@ -89,15 +135,17 @@ impl Bus for ApuBus { } fn read_and_tick(&mut self, addr: Address) -> u8 { + self.cycles += 1; self.read(addr.offset) } fn write_and_tick(&mut self, addr: Address, data: u8) { - self.write(addr.offset, data) + self.cycles += 1; + self.write(addr.offset, data); } - fn add_io_cycles(&mut self, _cycles: usize) { - todo!() + fn add_io_cycles(&mut self, cycles: usize) { + self.cycles += cycles as u64; } fn fired_nmi(&mut self) -> bool { diff --git a/aliusnes/src/apu/spc700/cpu.rs b/aliusnes/src/apu/spc700/cpu.rs index 1f717c0..6ddb541 100644 --- a/aliusnes/src/apu/spc700/cpu.rs +++ b/aliusnes/src/apu/spc700/cpu.rs @@ -31,7 +31,7 @@ impl Cpu { accumulator: 0x00, index_x: 0x00, index_y: 0x00, - program_counter: 0x00, + program_counter: 0xFFC0, stack_pointer: 0x00, status: Status(0), paused: false, diff --git a/aliusnes/src/bus/dma.rs b/aliusnes/src/bus/dma.rs index ccc45e0..539cb73 100644 --- a/aliusnes/src/bus/dma.rs +++ b/aliusnes/src/bus/dma.rs @@ -129,7 +129,7 @@ impl Access for Dma { } } - fn write(&mut self, addr: u16, data: u8) { + fn write(&mut self, addr: u16, data: u8, _: u64) { match addr { 0x420B => self.enable_channels = data, 0x420C => self.h_enable_channels = data, diff --git a/aliusnes/src/bus/math.rs b/aliusnes/src/bus/math.rs index 570007a..627f4d1 100644 --- a/aliusnes/src/bus/math.rs +++ b/aliusnes/src/bus/math.rs @@ -50,7 +50,7 @@ impl Access for Math { } } - fn write(&mut self, addr: u16, data: u8) { + fn write(&mut self, addr: u16, data: u8, _: u64) { match addr { 0x4202 => self.factor_a = data, 0x4203 => { diff --git a/aliusnes/src/bus/mod.rs b/aliusnes/src/bus/mod.rs index 6d4ae10..3628fea 100644 --- a/aliusnes/src/bus/mod.rs +++ b/aliusnes/src/bus/mod.rs @@ -67,5 +67,5 @@ pub(crate) trait Bus { pub(crate) trait Access { fn read(&mut self, addr: u16, time: u64) -> Option; - fn write(&mut self, addr: u16, data: u8); + fn write(&mut self, addr: u16, data: u8, time: u64); } diff --git a/aliusnes/src/bus/system_bus.rs b/aliusnes/src/bus/system_bus.rs index 653f2f5..34c246b 100644 --- a/aliusnes/src/bus/system_bus.rs +++ b/aliusnes/src/bus/system_bus.rs @@ -11,7 +11,7 @@ use crate::utils::int_traits::ManipulateU16; pub struct SystemBus { mdr: u8, fast_rom_enabled: bool, - apu: Apu, + pub apu: Apu, cart: Cart, pub dma: Dma, math: Math, @@ -25,7 +25,7 @@ impl SystemBus { Self { mdr: 0, fast_rom_enabled: false, - apu: Apu::new(), + apu: Apu::new(cart.model), ppu: Ppu::new(cart.model), scheduler: Scheduler::new(), cart, @@ -42,7 +42,7 @@ impl SystemBus { pub fn read_b(&mut self, addr: u16) -> u8 { if let Some(val) = match addr.low_byte() { 0x34..=0x3F => self.ppu.read(addr, self.scheduler.cycles), - 0x40..=0x43 => self.apu.read(addr, 0), + 0x40..=0x43 => self.apu.read(addr, self.scheduler.cycles), 0x80 => self.wram.read(addr, 0), _ => None, } { @@ -105,9 +105,9 @@ impl SystemBus { pub fn write_b(&mut self, addr: u16, data: u8) { match addr.low_byte() { - 0x00..=0x33 => self.ppu.write(addr, data), - 0x40..=0x43 => self.apu.write(addr, data), - 0x80..=0x83 => self.wram.write(addr, data), + 0x00..=0x33 => self.ppu.write(addr, data, 0), + 0x40..=0x43 => self.apu.write(addr, data, self.scheduler.cycles), + 0x80..=0x83 => self.wram.write(addr, data, 0), _ => println!("Tried to write at {addr:#0x} val: {data:#04x}"), } } @@ -123,12 +123,12 @@ impl SystemBus { 0x40..=0x43 if !DMA => { return match page { 0x4200 => self.ppu.write_nmitien(data), - 0x4202..=0x4206 => self.math.write(page, data), + 0x4202..=0x4206 => self.math.write(page, data, 0), 0x4207 => self.ppu.set_h_timer_low(data), 0x4208 => self.ppu.set_h_timer_high(data), 0x4209 => self.ppu.set_v_timer_low(data), 0x420A => self.ppu.set_v_timer_high(data), - 0x420B | 0x420C | 0x4300..=0x437f => self.dma.write(page, data), + 0x420B | 0x420C | 0x4300..=0x437f => self.dma.write(page, data, 0), 0x420D => self.fast_rom_enabled = data & 1 != 0, _ => println!("Tried to write at {page:#0x} val: {data:#04x}"), }; diff --git a/aliusnes/src/bus/wram.rs b/aliusnes/src/bus/wram.rs index 196af04..27db55b 100644 --- a/aliusnes/src/bus/wram.rs +++ b/aliusnes/src/bus/wram.rs @@ -45,7 +45,7 @@ impl Access for Wram { Some(data) } - fn write(&mut self, addr: u16, data: u8) { + fn write(&mut self, addr: u16, data: u8, _: u64) { match addr { 0x2180 => self.write_to_wm_addr(data), 0x2181 => self.wm_addl(data), diff --git a/aliusnes/src/emu.rs b/aliusnes/src/emu.rs index 2e0e418..388e8d0 100644 --- a/aliusnes/src/emu.rs +++ b/aliusnes/src/emu.rs @@ -22,6 +22,7 @@ impl Emu { emu.bus .scheduler .add_event(Event::Ppu(PpuEvent::NewScanline), 0); + emu.bus.scheduler.add_event(Event::Apu, 0); emu } @@ -44,6 +45,7 @@ impl Emu { while let Some((event, time)) = bus.scheduler.pop_event() { match event { Event::Ppu(ppu_event) => bus.ppu.handle_event(&mut bus.scheduler, ppu_event, time), + Event::Apu => bus.apu.handle_event(&mut bus.scheduler, time), } } } @@ -73,6 +75,7 @@ impl Emu { .ppu .handle_event(&mut self.bus.scheduler, ppu_event, time); }, + Event::Apu => self.bus.apu.handle_event(&mut self.bus.scheduler, time), } } } diff --git a/aliusnes/src/ppu.rs b/aliusnes/src/ppu.rs index 1ac7e02..bc1ccd6 100644 --- a/aliusnes/src/ppu.rs +++ b/aliusnes/src/ppu.rs @@ -191,7 +191,7 @@ impl Access for Ppu { } } - fn write(&mut self, addr: u16, data: u8) { + fn write(&mut self, addr: u16, data: u8, _: u64) { let nibble = addr.low_byte() as usize; match nibble { 0x00 => self.ini_display = IniDisplay(data), diff --git a/aliusnes/src/scheduler.rs b/aliusnes/src/scheduler.rs index 17a064e..929569e 100644 --- a/aliusnes/src/scheduler.rs +++ b/aliusnes/src/scheduler.rs @@ -9,6 +9,7 @@ pub enum PpuEvent { #[derive(Clone, Copy)] pub enum Event { + Apu, Ppu(PpuEvent), } @@ -16,6 +17,7 @@ impl Event { fn index(self) -> usize { match self { Event::Ppu(_) => 1, + Event::Apu => 2, } } } From c93c2bcbe566d00e59b678aacb549e4a0d1eb7df Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:08:01 +0200 Subject: [PATCH 03/10] apu: added dummy dsp and timers --- aliusnes/src/apu/dsp/mod.rs | 26 +++++++++++++++ aliusnes/src/apu/mod.rs | 55 +++++++++++++++++++++----------- aliusnes/src/apu/spc700/mod.rs | 1 + aliusnes/src/apu/spc700/timer.rs | 33 +++++++++++++++++++ 4 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 aliusnes/src/apu/dsp/mod.rs create mode 100644 aliusnes/src/apu/spc700/timer.rs diff --git a/aliusnes/src/apu/dsp/mod.rs b/aliusnes/src/apu/dsp/mod.rs new file mode 100644 index 0000000..8620bf7 --- /dev/null +++ b/aliusnes/src/apu/dsp/mod.rs @@ -0,0 +1,26 @@ +pub(crate) struct Dsp { + dsp_addr: u8, +} + +impl Dsp { + pub(crate) fn new() -> Dsp { + Dsp { dsp_addr: 0 } + } + + pub(crate) fn set_dsp_addr(&mut self, value: u8) { + self.dsp_addr = value; + } + + pub(crate) fn read_dsp_addr(&self) -> u8 { + self.dsp_addr + } + + pub(crate) fn read(&self) -> u8 { + // TODO read from registers + self.dsp_addr + } + + pub(crate) fn write(&mut self, _: u8) { + // TODO write to registers + } +} diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index 97d4db9..dde9b86 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -1,8 +1,11 @@ +use crate::apu::dsp::Dsp; use crate::apu::spc700::Spc700; +use crate::apu::spc700::timer::Timer; use crate::bus::{Access, Address, Bus}; use crate::cart::info::Model; use crate::scheduler::{Event, Scheduler}; +mod dsp; pub mod spc700; #[cfg(feature = "apu")] @@ -29,7 +32,10 @@ struct ApuBus { aram: [u8; 0x10000], apuio: [u8; 4], cpuio: [u8; 4], + bootrom_enabled: bool, cycles: u64, + dsp: Dsp, + timers: [Timer; 3], } impl Apu { @@ -40,7 +46,10 @@ impl Apu { aram: [0; 0x10000], apuio: [0; 4], cpuio: [0; 4], + bootrom_enabled: true, cycles: 0, + dsp: Dsp::new(), + timers: [Timer::new(); 3], }, model, } @@ -87,6 +96,21 @@ impl Access for Apu { } impl ApuBus { + fn write_control(&mut self, data: u8) { + for (i, timer) in self.timers.iter_mut().enumerate() { + timer.set_enabled(data & (1 << i) != 0); + } + + if (data & (1 << 4)) != 0 { + self.apuio[0..=1].fill(0); + } + if (data & (1 << 5)) != 0 { + self.apuio[2..=3].fill(0); + } + + self.bootrom_enabled = (data & 0x80) != 0; + } + fn read(&self, addr: u16) -> u8 { match addr { // write-only area @@ -94,34 +118,29 @@ impl ApuBus { println!("Attempted to read write-only registers, returning 0"); 0 }, - // dsp registers - 0x00F2 | 0x00F3 => { - println!("Attempted to read dsp registers, returning 0"); - 0 - }, + 0x00F2 => self.dsp.read_dsp_addr(), + 0x00F3 => self.dsp.read(), 0x00F4..=0x00F7 => self.apuio[addr as usize & 3], - // timer registers - 0x00FD..=0x00FF => { - println!("Attempted to read timer registers, returning 0"); - 0 - }, + 0x00FD => self.timers[0].timer_output(), + 0x00FE => self.timers[1].timer_output(), + 0x00FF => self.timers[2].timer_output(), + 0xFFC0..=0xFFFF if self.bootrom_enabled => IPL_ROM[addr as usize & 0x3F], _ => self.aram[addr as usize], } } fn write(&mut self, addr: u16, data: u8) { match addr { - 0x00F0 => todo!(), - 0x00F1 => todo!(), - // dsp registers - 0x00F2 | 0x00F3 => println!("Attempted to write dsp registers"), + 0x00F0 => println!("Tried to write TEST: {data:#04x}"), + 0x00F1 => self.write_control(data), + 0x00F2 => self.dsp.set_dsp_addr(data), + 0x00F3 => self.dsp.write(data), 0x00F4..=0x00F7 => { self.cpuio[addr as usize & 3] = data; }, - // timer registers - 0x00FA..=0x00FC => { - println!("Attempted to write timer registers"); - }, + 0x00FA => self.timers[0].set_timer_target(data), + 0x00FB => self.timers[1].set_timer_target(data), + 0x00FC => self.timers[2].set_timer_target(data), // read-only area 0x00FD..=0x00FF => (), _ => self.aram[addr as usize] = data, diff --git a/aliusnes/src/apu/spc700/mod.rs b/aliusnes/src/apu/spc700/mod.rs index 247ce41..bcbe472 100644 --- a/aliusnes/src/apu/spc700/mod.rs +++ b/aliusnes/src/apu/spc700/mod.rs @@ -6,6 +6,7 @@ mod addressing; mod cpu; mod instructions; mod opcode; +pub(super) mod timer; pub(crate) struct Spc700 { cpu: Cpu, diff --git a/aliusnes/src/apu/spc700/timer.rs b/aliusnes/src/apu/spc700/timer.rs new file mode 100644 index 0000000..a2ae699 --- /dev/null +++ b/aliusnes/src/apu/spc700/timer.rs @@ -0,0 +1,33 @@ +#[derive(Clone, Copy)] +pub struct Timer { + enabled: bool, + timer_target: u8, + timer_output: u8, +} + +impl Timer { + pub(crate) fn new() -> Timer { + Timer { + enabled: false, + timer_target: 0, + timer_output: 0, + } + } + + pub(crate) fn set_enabled(&mut self, enabled: bool) { + // A transition from clear to set (0 -> 1) will reset the timer's internal counter and TxOUT to 0. + if !self.enabled && enabled {} + self.enabled = enabled; + } + + pub(crate) fn set_timer_target(&mut self, value: u8) { + // When enabled via $F1, the 3 timers will internally count at a rate of 8 KHz (timers 0,1) or 64 KHz (timer 2), + // and when this interval value has been exceeded, they will increment their external counter result ($FD-FF) and begin again. + self.timer_target = value; + self.timer_output = value; + } + + pub(crate) fn timer_output(&self) -> u8 { + self.timer_output + } +} From 0ef4afe07c67859672539dff037e4cd1479d2e6a Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:00:12 +0200 Subject: [PATCH 04/10] apu: implemented dsp registers --- aliusnes/src/apu/dsp/mod.rs | 133 ++++++++++++++++++++++++++++++++-- aliusnes/src/apu/dsp/voice.rs | 81 +++++++++++++++++++++ 2 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 aliusnes/src/apu/dsp/voice.rs diff --git a/aliusnes/src/apu/dsp/mod.rs b/aliusnes/src/apu/dsp/mod.rs index 8620bf7..3d4bda7 100644 --- a/aliusnes/src/apu/dsp/mod.rs +++ b/aliusnes/src/apu/dsp/mod.rs @@ -1,26 +1,143 @@ +use crate::apu::dsp::voice::Voice; + +mod voice; + +bitfield! { + #[derive(Clone, Copy)] + struct Flags(u8) { + pub noise_frequency: u8 @ 0..=4, + pub disable_echo_write: bool @ 5, + pub mute_all: bool @ 6, + pub soft_reset: bool @ 7, + } +} + pub(crate) struct Dsp { - dsp_addr: u8, + dsp_addr: usize, + voices: [Voice; 8], + left_main_channel_volume: i8, + right_main_channel_volume: i8, + left_echo_volume: i8, + right_echo_volume: i8, + key_on: u8, + key_off: u8, + flags: Flags, + end_flag_channel_mask: u8, + echo_feedback: i8, + + unused: u8, + + pitch_modulation_channel_mask: u8, + noise_enabled_channel_mask: u8, + echo_enabled_channel_mask: u8, + + pointer_to_sample_directory: u8, + pointer_to_echo_buffer: u8, + echo_delay: u8, + echo_filter_coefficients: [i8; 8], } impl Dsp { pub(crate) fn new() -> Dsp { - Dsp { dsp_addr: 0 } + Dsp { + dsp_addr: 0, + voices: [Voice::new(); 8], + left_main_channel_volume: 0, + right_main_channel_volume: 0, + left_echo_volume: 0, + right_echo_volume: 0, + key_on: 0, + key_off: 0, + flags: Flags(0), + end_flag_channel_mask: 0, + echo_feedback: 0, + unused: 0, + pitch_modulation_channel_mask: 0, + noise_enabled_channel_mask: 0, + echo_enabled_channel_mask: 0, + pointer_to_sample_directory: 0, + pointer_to_echo_buffer: 0, + echo_delay: 0, + echo_filter_coefficients: [0; 8], + } } pub(crate) fn set_dsp_addr(&mut self, value: u8) { - self.dsp_addr = value; + self.dsp_addr = value.into(); } pub(crate) fn read_dsp_addr(&self) -> u8 { - self.dsp_addr + self.dsp_addr as u8 } pub(crate) fn read(&self) -> u8 { - // TODO read from registers - self.dsp_addr + let addr = self.dsp_addr & 0x7F; + let index = addr >> 4; + + match addr & 0xF { + low_nibble @ 0x0..=0x9 => return self.voices[index].read(low_nibble), + 0xC => match index { + 0x0 => return self.left_main_channel_volume as u8, + 0x1 => return self.right_main_channel_volume as u8, + 0x2 => return self.left_echo_volume as u8, + 0x3 => return self.right_echo_volume as u8, + 0x4 => return self.key_on, + 0x5 => return self.key_off, + 0x6 => return self.flags.0, + 0x7 => return self.end_flag_channel_mask, + _ => (), + }, + 0xD => match index { + 0x0 => return self.echo_feedback as u8, + 0x1 => return self.unused, + 0x2 => return self.pitch_modulation_channel_mask, + 0x3 => return self.noise_enabled_channel_mask, + 0x4 => return self.echo_enabled_channel_mask, + 0x5 => return self.pointer_to_sample_directory, + 0x6 => return self.pointer_to_echo_buffer, + 0x7 => return self.echo_delay, + _ => (), + }, + 0xF => return self.echo_filter_coefficients[index] as u8, + _ => (), + } + + println!("Read from invalid DSP register: {:#04X}", self.dsp_addr); + 0 } - pub(crate) fn write(&mut self, _: u8) { - // TODO write to registers + pub(crate) fn write(&mut self, data: u8) { + if self.dsp_addr >= 0x80 { + return; + } + let addr = self.dsp_addr & 0x7F; + let index = addr >> 4; + + match addr & 0xF { + low_nibble @ 0x0..=0x7 => self.voices[index].write(low_nibble, data), + 0xC => match index { + 0x0 => self.left_main_channel_volume = data as i8, + 0x1 => self.right_main_channel_volume = data as i8, + 0x2 => self.left_echo_volume = data as i8, + 0x3 => self.right_echo_volume = data as i8, + 0x4 => self.key_on = data, + 0x5 => self.key_off = data, + 0x6 => self.flags = Flags(data), + _ => (), + }, + 0xD => match index { + 0x0 => self.echo_feedback = data as i8, + 0x1 => self.unused = data, + 0x2 => self.pitch_modulation_channel_mask = data, + 0x3 => self.noise_enabled_channel_mask = data, + 0x4 => self.echo_enabled_channel_mask = data, + 0x5 => self.pointer_to_sample_directory = data, + 0x6 => self.pointer_to_echo_buffer = data, + 0x7 => self.echo_delay = data, + _ => (), + }, + 0xF => self.echo_filter_coefficients[index] = data as i8, + _ => (), + } } } diff --git a/aliusnes/src/apu/dsp/voice.rs b/aliusnes/src/apu/dsp/voice.rs new file mode 100644 index 0000000..add1d0a --- /dev/null +++ b/aliusnes/src/apu/dsp/voice.rs @@ -0,0 +1,81 @@ +use std::hint::unreachable_unchecked; + +use crate::utils::int_traits::ManipulateU16; + +bitfield! { + #[derive(Clone, Copy)] + struct Adsr(u16) { + attack_rate: u8 @ 0..=3, + decay_rate: u8 @ 4..=6, + enabled: bool @ 7, + sustain_rate: u8 @ 8..=12, + sustain_level: u8 @ 13..=15, + } +} + +bitfield! { + #[derive(Clone, Copy)] + struct Gain(u8) { + fixed_volume: u8 @ 0..=6, + rate: u8 @ 0..=4, + mode: u8 @ 5..=6, + use_custom: bool @ 7, + } +} + +#[derive(Clone, Copy)] +pub struct Voice { + left_channel_volume: i8, + right_channel_volume: i8, + sample_pitch: u16, + sample_source_entry: u8, + adsr: Adsr, + gain: Gain, + current_envelope: u8, + current_sample: u8, +} + +impl Voice { + pub(crate) fn new() -> Voice { + Voice { + left_channel_volume: 0, + right_channel_volume: 0, + sample_pitch: 0, + sample_source_entry: 0, + adsr: Adsr(0), + gain: Gain(0), + current_envelope: 0, + current_sample: 0, + } + } + + pub(crate) fn read(&self, low_nibble: usize) -> u8 { + match low_nibble { + 0x0 => self.left_channel_volume as u8, + 0x1 => self.right_channel_volume as u8, + 0x2 => self.sample_pitch.low_byte(), + 0x3 => self.sample_pitch.high_byte(), + 0x4 => self.sample_source_entry, + 0x5 => self.adsr.0.low_byte(), + 0x6 => self.adsr.0.high_byte(), + 0x7 => self.gain.0, + 0x8 => self.current_envelope, + 0x9 => self.current_sample, + _ => unsafe { unreachable_unchecked() }, + } + } + + pub(crate) fn write(&mut self, low_nibble: usize, data: u8) { + match low_nibble { + 0x0 => self.left_channel_volume = data as i8, + 0x1 => self.right_channel_volume = data as i8, + 0x2 => self.sample_pitch.set_low_byte(data), + 0x3 => self.sample_pitch.set_high_byte(data), + 0x4 => self.sample_source_entry = data, + 0x5 => self.adsr.0.set_low_byte(data), + 0x6 => self.adsr.0.set_high_byte(data), + 0x7 => self.gain = Gain(data), + _ => unsafe { unreachable_unchecked() }, + } + } +} From 147e6c8021501fe19ff2b77618799da8f3af8d40 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Sat, 6 Sep 2025 12:52:23 +0200 Subject: [PATCH 05/10] chore: suppress clippy lint dead code on some places --- aliusnes/src/apu/spc700/opcode.rs | 4 +++- aliusnes/src/w65c816/cpu.rs | 1 + aliusnes/src/w65c816/opcode.rs | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/aliusnes/src/apu/spc700/opcode.rs b/aliusnes/src/apu/spc700/opcode.rs index 8ddeb4a..c780985 100644 --- a/aliusnes/src/apu/spc700/opcode.rs +++ b/aliusnes/src/apu/spc700/opcode.rs @@ -4,8 +4,10 @@ use crate::bus::Bus; #[derive(Clone, Copy)] pub(crate) struct Meta { - pub code: u8, + #[allow(dead_code)] pub mnemonic: &'static str, + #[allow(dead_code)] + pub code: u8, pub mode: AddressingMode, } diff --git a/aliusnes/src/w65c816/cpu.rs b/aliusnes/src/w65c816/cpu.rs index 0f80812..a734553 100644 --- a/aliusnes/src/w65c816/cpu.rs +++ b/aliusnes/src/w65c816/cpu.rs @@ -6,6 +6,7 @@ use crate::w65c816::regsize::RegSize; pub enum Vector { Cop, Brk, + #[allow(dead_code)] Abort, Nmi, Irq, diff --git a/aliusnes/src/w65c816/opcode.rs b/aliusnes/src/w65c816/opcode.rs index 8686937..7a4b93e 100644 --- a/aliusnes/src/w65c816/opcode.rs +++ b/aliusnes/src/w65c816/opcode.rs @@ -6,7 +6,9 @@ use crate::w65c816::{Cpu, W65C816}; #[derive(Clone, Copy)] pub(crate) struct Meta { + #[allow(dead_code)] pub code: u8, + #[allow(dead_code)] mnemonic: &'static str, pub mode: AddressingMode, } From 8dbff6e211be6c6c6007966fe0dc93aaeb492480 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Sun, 31 May 2026 16:19:44 +0200 Subject: [PATCH 06/10] apu: box aram --- aliusnes/src/apu/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index dde9b86..3a3b97e 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -29,7 +29,7 @@ pub struct Apu { } struct ApuBus { - aram: [u8; 0x10000], + aram: Box<[u8; 0x10000]>, apuio: [u8; 4], cpuio: [u8; 4], bootrom_enabled: bool, @@ -43,7 +43,7 @@ impl Apu { Apu { spc700: Spc700::new(), bus: ApuBus { - aram: [0; 0x10000], + aram: vec![0; 0x10000].into_boxed_slice().try_into().unwrap(), apuio: [0; 4], cpuio: [0; 4], bootrom_enabled: true, From 9cd905f085c0dc119a464929ca091c8c998bd790 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Sun, 31 May 2026 18:13:04 +0200 Subject: [PATCH 07/10] apu: some cleanup --- aliusnes/src/apu/mod.rs | 12 ++++-------- aliusnes/src/apu/spc700/addressing.rs | 2 +- aliusnes/src/apu/spc700/cpu.rs | 4 ++-- aliusnes/src/apu/spc700/instructions.rs | 6 +++--- aliusnes/src/apu/spc700/opcode.rs | 4 ++-- aliusnes/src/bus/mod.rs | 5 ++++- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index 3a3b97e..2e4cd5b 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -6,7 +6,7 @@ use crate::cart::info::Model; use crate::scheduler::{Event, Scheduler}; mod dsp; -pub mod spc700; +mod spc700; #[cfg(feature = "apu")] static IPL_ROM: &[u8; 0x40] = include_bytes!("spc700/ipl_boot.rom"); @@ -55,7 +55,7 @@ impl Apu { } } - pub(crate) fn catch_up_to_master(&mut self, time: u64) { + fn catch_up_to_master(&mut self, time: u64) { let Apu { ref mut bus, ref mut spc700, @@ -63,8 +63,8 @@ impl Apu { } = self; let target_time = match model { - Model::Ntsc => time as u128 * SPC700_CLOCK / NTSC_MASTER_CLOCK, - Model::Pal => time as u128 * SPC700_CLOCK / PAL_MASTER_CLOCK, + Model::Ntsc => u128::from(time) * SPC700_CLOCK / NTSC_MASTER_CLOCK, + Model::Pal => u128::from(time) * SPC700_CLOCK / PAL_MASTER_CLOCK, }; while bus.cycles < target_time as u64 { @@ -149,10 +149,6 @@ impl ApuBus { } impl Bus for ApuBus { - fn peek_at(&self, _addr: Address) -> Option { - todo!() - } - fn read_and_tick(&mut self, addr: Address) -> u8 { self.cycles += 1; self.read(addr.offset) diff --git a/aliusnes/src/apu/spc700/addressing.rs b/aliusnes/src/apu/spc700/addressing.rs index 4779cf8..2561420 100644 --- a/aliusnes/src/apu/spc700/addressing.rs +++ b/aliusnes/src/apu/spc700/addressing.rs @@ -134,7 +134,7 @@ impl Cpu { AddressingMode::Psw => self.status.0, AddressingMode::Immediate => self.get_imm(bus), AddressingMode::AbsoluteBooleanBit => { - let addr_bit = u16::from_le_bytes([self.get_imm(bus), self.get_imm(bus)]); + let addr_bit = self.abs(bus); let val = bus.read_and_tick((addr_bit & 0x1FFF).into()); val & 1 << (addr_bit >> 13) diff --git a/aliusnes/src/apu/spc700/cpu.rs b/aliusnes/src/apu/spc700/cpu.rs index 6ddb541..a22306d 100644 --- a/aliusnes/src/apu/spc700/cpu.rs +++ b/aliusnes/src/apu/spc700/cpu.rs @@ -47,7 +47,7 @@ impl Cpu { self.status.set_zero(value == 0); } - pub fn read_16(&mut self, bus: &mut B, addr: u16) -> u16 { + pub fn read_word(&self, bus: &mut B, addr: u16) -> u16 { u16::from_le_bytes([ bus.read_and_tick(addr.into()), bus.read_and_tick(addr.wrapping_add(1).into()), @@ -127,7 +127,7 @@ impl Cpu { AddressingMode::X => self.index_x = f(self, self.index_x), AddressingMode::Y => self.index_y = f(self, self.index_y), AddressingMode::AbsoluteBooleanBit => { - let addr_bit = u16::from_le_bytes([self.get_imm(bus), self.get_imm(bus)]); + let addr_bit = self.abs(bus); let page = addr_bit & 0x1FFF; let bit_pos = addr_bit >> 13; diff --git a/aliusnes/src/apu/spc700/instructions.rs b/aliusnes/src/apu/spc700/instructions.rs index 02d68f6..d6cd672 100644 --- a/aliusnes/src/apu/spc700/instructions.rs +++ b/aliusnes/src/apu/spc700/instructions.rs @@ -94,7 +94,7 @@ impl Spc700 { cpu.do_push(bus, cpu.program_counter.low_byte()); cpu.do_push(bus, cpu.status.0); - cpu.program_counter = cpu.read_16(bus, 0xFFDE); + cpu.program_counter = cpu.read_word(bus, 0xFFDE); cpu.status.set_irq_enabled(false); cpu.status.set_break_(true); } @@ -287,7 +287,7 @@ impl Spc700 { if let AddressingMode::Absolute = mode { cpu.program_counter = value; } else { - cpu.program_counter = cpu.read_16(bus, value); + cpu.program_counter = cpu.read_word(bus, value); } } @@ -532,7 +532,7 @@ impl Spc700 { cpu.do_push(bus, cpu.program_counter.low_byte()); let vector_addr = 0xFFDE - 2 * u16::from(INDEX); - cpu.program_counter = cpu.read_16(bus, vector_addr); + cpu.program_counter = cpu.read_word(bus, vector_addr); } pub fn tclr1(cpu: &mut Cpu, bus: &mut B, mode: AddressingMode) { diff --git a/aliusnes/src/apu/spc700/opcode.rs b/aliusnes/src/apu/spc700/opcode.rs index c780985..6a6ecb7 100644 --- a/aliusnes/src/apu/spc700/opcode.rs +++ b/aliusnes/src/apu/spc700/opcode.rs @@ -4,10 +4,10 @@ use crate::bus::Bus; #[derive(Clone, Copy)] pub(crate) struct Meta { - #[allow(dead_code)] - pub mnemonic: &'static str, #[allow(dead_code)] pub code: u8, + #[allow(dead_code)] + mnemonic: &'static str, pub mode: AddressingMode, } diff --git a/aliusnes/src/bus/mod.rs b/aliusnes/src/bus/mod.rs index 3628fea..aaa925b 100644 --- a/aliusnes/src/bus/mod.rs +++ b/aliusnes/src/bus/mod.rs @@ -57,7 +57,10 @@ impl From
for usize { } pub(crate) trait Bus { - fn peek_at(&self, addr: Address) -> Option; + #[allow(dead_code)] + fn peek_at(&self, _addr: Address) -> Option { + None + } fn read_and_tick(&mut self, addr: Address) -> u8; fn write_and_tick(&mut self, addr: Address, data: u8); fn add_io_cycles(&mut self, cycles: usize); From 514518dbc75f55f0f59b786839916dbe424ea0d9 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:05:30 +0200 Subject: [PATCH 08/10] apu: Add brr decoding PoC --- aliusnes/src/apu/dsp/mod.rs | 3 ++ aliusnes/src/apu/dsp/voice.rs | 59 +++++++++++++++++++++++++++++++++++ aliusnes/src/apu/mod.rs | 2 ++ 3 files changed, 64 insertions(+) diff --git a/aliusnes/src/apu/dsp/mod.rs b/aliusnes/src/apu/dsp/mod.rs index 3d4bda7..20e3e1b 100644 --- a/aliusnes/src/apu/dsp/mod.rs +++ b/aliusnes/src/apu/dsp/mod.rs @@ -140,4 +140,7 @@ impl Dsp { _ => (), } } + + #[allow(dead_code)] + pub(crate) fn output_sample(&mut self, _ram: &[u8]) {} } diff --git a/aliusnes/src/apu/dsp/voice.rs b/aliusnes/src/apu/dsp/voice.rs index add1d0a..3fdc8e2 100644 --- a/aliusnes/src/apu/dsp/voice.rs +++ b/aliusnes/src/apu/dsp/voice.rs @@ -23,6 +23,22 @@ bitfield! { } } +bitfield! { + struct ControlBlock(u8) { + end: bool @ 0, + loop_sample: bool @ 1, + filter: u8 @ 2..=3, + left_shift: u8 @ 4..=7, + } +} + +const FILTER_TABLE: [[f32; 2]; 4] = [ + [0.0, 0.0], + [15.0 / 16.0, 0.0], + [61.0 / 32.0, 15.0 / 16.0], + [115.0 / 64.0, 13.0 / 16.0], +]; + #[derive(Clone, Copy)] pub struct Voice { left_channel_volume: i8, @@ -78,4 +94,47 @@ impl Voice { _ => unsafe { unreachable_unchecked() }, } } + + #[allow(dead_code)] + fn sample_entry(&self, dir: u16) -> u16 { + dir * 0x100 + self.sample_source_entry as u16 * 4 + } + + #[allow(dead_code)] + fn decode_brr_block(encoded: [u8; 9], old: &mut f32, older: &mut f32) -> [i16; 16] { + // First block is a control block + let control = ControlBlock(encoded[0]); + + let mut decoded = [0; 16]; + + let control_filter = control.filter() as usize; + + // Samples are stored from encoded[1] to encoded[8] + for i in 0..16 { + // Sample values range from -8 to +7 + // and are stored high-endian. + let sample_byte = encoded[1 + (i / 2)]; + let nibble = (sample_byte >> (4 * ((i & 1) ^ 1))) & 0x0F; + + // Sign extend from 4 bits to 16 bits. + let raw_sample: i16 = (((nibble as i8) << 4) >> 4).into(); + + // Apply left shift value to convert to 15bit sample. + // TODO when shift amount is > 12, it seems it behaves as + // shift=12 and nibble=(nibble SAR 3) + let shifted_sample = (raw_sample << control.left_shift()) >> 1; + + // Apply control filter to the sample + let filtered = f32::from(shifted_sample) + + *old * FILTER_TABLE[control_filter][0] + + *older * FILTER_TABLE[control_filter][1]; + + *older = *old; + *old = filtered; + + decoded[i] = filtered as i16; + } + + decoded + } } diff --git a/aliusnes/src/apu/mod.rs b/aliusnes/src/apu/mod.rs index 2e4cd5b..d942c39 100644 --- a/aliusnes/src/apu/mod.rs +++ b/aliusnes/src/apu/mod.rs @@ -75,6 +75,8 @@ impl Apu { pub(crate) fn handle_event(&mut self, scheduler: &mut Scheduler, time: u64) { self.catch_up_to_master(time); + // self.bus.dsp.output_sample(self.bus.aram.as_slice()); + let period = match self.model { Model::Ntsc => APU_EVENT_PERIOD_NTSC, Model::Pal => APU_EVENT_PERIOD_PAL, From f9d91a9be1e9bf76455eec8cd6592d65e8e03432 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:21:32 +0200 Subject: [PATCH 09/10] CI: exclude apu feature --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68aa5ab..d5e0c3f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: run: cargo fmt --all -- --check - name: Clippy check - run: cargo clippy --all-targets --all-features + run: cargo clippy --all-targets --no-default-features --features "log trace" tests: runs-on: ubuntu-latest @@ -39,4 +39,4 @@ jobs: toolchain: nightly - name: Run tests - run: cargo test --package aliusnes + run: cargo test --package aliusnes --no-default-features From f10c667ce98fccfc93e1cd53d80baad25385b543 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:37:35 +0200 Subject: [PATCH 10/10] apu: fix-up timer code --- aliusnes/src/apu/spc700/timer.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/aliusnes/src/apu/spc700/timer.rs b/aliusnes/src/apu/spc700/timer.rs index a2ae699..d871571 100644 --- a/aliusnes/src/apu/spc700/timer.rs +++ b/aliusnes/src/apu/spc700/timer.rs @@ -15,14 +15,16 @@ impl Timer { } pub(crate) fn set_enabled(&mut self, enabled: bool) { - // A transition from clear to set (0 -> 1) will reset the timer's internal counter and TxOUT to 0. - if !self.enabled && enabled {} + // A transition from clear to set (0 -> 1) will reset the timer's internal counter and + // TxOUT to 0. + // if !self.enabled && enabled {} self.enabled = enabled; } pub(crate) fn set_timer_target(&mut self, value: u8) { - // When enabled via $F1, the 3 timers will internally count at a rate of 8 KHz (timers 0,1) or 64 KHz (timer 2), - // and when this interval value has been exceeded, they will increment their external counter result ($FD-FF) and begin again. + // When enabled via $F1, the 3 timers will internally count at a rate of 8 KHz (timers 0,1) + // or 64 KHz (timer 2), and when this interval value has been exceeded, they will increment + // their external counter result ($FD-FF) and begin again. self.timer_target = value; self.timer_output = value; }