Skip to content

Commit

Permalink
SPC700: implement timers
Browse files Browse the repository at this point in the history
  • Loading branch information
twvd committed Nov 27, 2023
1 parent 87242b8 commit 4e2746e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 5 deletions.
43 changes: 39 additions & 4 deletions src/snes/apu/apubus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::snes::bus::{Bus, BusMember};
use crate::snes::cpu_spc700::cpu::{SpcAddress, SPC_ADDRESS_MASK};
use crate::tickable::{Tickable, Ticks};

use super::timers::{Timer, APU_TIMERS};
use super::ApuPorts;

const APU_RAM_SIZE: usize = 64 * 1024;
Expand All @@ -17,6 +18,13 @@ pub struct Apubus {
ram: [u8; APU_RAM_SIZE],
rom: [u8; APU_ROM_SIZE],
ports: Rc<RefCell<ApuPorts>>,

/// Timers
timers: [Timer; APU_TIMERS],

/// Bitmask of enabled timers
/// (also 0x00F1, bit 0-2)
timers_enabled: u8,
}

impl Apubus {
Expand All @@ -25,6 +33,16 @@ impl Apubus {
ram: [0; APU_RAM_SIZE],
rom: rom.try_into().unwrap(),
ports,

timers: [
// Timer 0, 8KHz (div = 128)
Timer::new(1_024_000 / 8_000),
// Timer 1, 8KHz (div = 128)
Timer::new(1_024_000 / 8_000),
// Timer 2, 64KHz (div = 16)
Timer::new(1_024_000 / 64_000),
],
timers_enabled: 0,
}
}
}
Expand All @@ -37,6 +55,9 @@ impl Bus<SpcAddress> for Apubus {
let ports = self.ports.borrow();
ports.apu[addr as usize - 0x00F4]
}
0x00FD => self.timers[0].get_cnt(),
0x00FE => self.timers[1].get_cnt(),
0x00FF => self.timers[2].get_cnt(),

// ROM (IPL)
// TODO mask setting!
Expand All @@ -50,25 +71,33 @@ impl Bus<SpcAddress> for Apubus {
0x00F1 => {
println!("APU control: {:02X}", val);

for t in 0..APU_TIMERS {
if (self.timers_enabled & val) & (1 << t) == 0 {
self.timers[t].reset();
}
}

self.timers_enabled = val & 0x03;

if val & (1 << 4) != 0 {
let mut ports = self.ports.borrow_mut();
println!("Clear input 0, 1");
ports.apu[0] = 0;
ports.apu[1] = 0;
}
if val & (1 << 5) != 0 {
let mut ports = self.ports.borrow_mut();
println!("Clear input 2, 3");
ports.apu[2] = 0;
ports.apu[3] = 0;
}
}
// Ports
0x00F4..=0x00F7 => {
let mut ports = self.ports.borrow_mut();
println!("APU port {:04X} to {:02X}", addr, val);
ports.cpu[addr as usize - 0x00F4] = val;
}
0x00FA => self.timers[0].set_top(val),
0x00FB => self.timers[1].set_top(val),
0x00FC => self.timers[2].set_top(val),
_ => (),
}

Expand All @@ -92,9 +121,15 @@ impl Bus<SpcAddress> for Apubus {
}

impl Tickable for Apubus {
fn tick(&mut self, _ticks: Ticks) -> Result<()> {
fn tick(&mut self, ticks: Ticks) -> Result<()> {
// This ticks at the speed of the APU CPU,
// not the master clock!

for t in 0..APU_TIMERS {
if self.timers_enabled & (1 << t) != 0 {
self.timers[t].tick(ticks);
}
}
Ok(())
}
}
1 change: 1 addition & 0 deletions src/snes/apu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod apubus;
pub mod timers;

use std::cell::RefCell;
use std::rc::Rc;
Expand Down
53 changes: 53 additions & 0 deletions src/snes/apu/timers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::tickable::Ticks;

pub const APU_TIMERS: usize = 3;

/// One APU timer
pub struct Timer {
/// The top register
top: usize,

/// Internal tick counter
ticks: usize,

/// Counter register (4-bit)
cnt: u8,

/// Divider
div: Ticks,
}

impl Timer {
pub fn new(div: Ticks) -> Self {
Self {
top: 0,
ticks: 0,
cnt: 0,
div,
}
}

pub fn reset(&mut self) {
self.ticks = 0;
self.cnt = 0;
}

pub fn tick(&mut self, ticks: Ticks) {
// This is supposed to be called at the APU frequency,
// the timer will scale down by its divider.

self.ticks += ticks;
if self.ticks / self.div >= usize::from(self.top) {
self.cnt = (self.cnt + 1) & 0x0F;
self.ticks -= self.top * self.div;
}
}

pub fn set_top(&mut self, top: u8) {
self.top = usize::from(top)
}

pub fn get_cnt(&self) -> u8 {
self.cnt
}
}
1 change: 0 additions & 1 deletion src/snes/bus/mainbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,6 @@ where
0x2140..=0x217F => {
let ch = (addr - 0x2140) % 4;
let mut ports = self.apu_ports.borrow_mut();
println!("CPU APU port {} to {:02X}", ch, val);
ports.apu[ch] = val;
Some(())
}
Expand Down

0 comments on commit 4e2746e

Please sign in to comment.