This repository has been archived on 2024-06-01. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/core/src/timer.rs

285 lines
8.3 KiB
Rust
Raw Normal View History

use super::dma::DmaController;
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::iodev::consts::*;
use super::sched::{EventType, Scheduler, SharedScheduler};
use super::sound::SoundController;
use num::FromPrimitive;
use serde::{Deserialize, Serialize};
const SHIFT_LUT: [usize; 4] = [0, 6, 8, 10];
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Timer {
// registers
pub ctl: TimerCtl,
pub data: u16,
pub initial_data: u16,
start_time: usize,
is_scheduled: bool,
irq: Interrupt,
interrupt_flags: SharedInterruptFlags,
timer_id: usize,
prescalar_shift: usize,
}
impl Timer {
pub fn new(timer_id: usize, interrupt_flags: SharedInterruptFlags) -> Timer {
if timer_id > 3 {
panic!("invalid timer id {}", timer_id);
}
Timer {
timer_id: timer_id,
irq: Interrupt::from_usize(timer_id + 3).unwrap(),
interrupt_flags,
data: 0,
ctl: TimerCtl(0),
initial_data: 0,
prescalar_shift: 0,
start_time: 0,
is_scheduled: false,
}
}
#[inline]
fn ticks_to_overflow(&self) -> u32 {
0x1_0000 - (self.data as u32)
}
#[inline]
fn sync_timer_data(&mut self, timestamp: usize) {
let ticks_passed = (timestamp - self.start_time) >> self.prescalar_shift;
self.data += ticks_passed as u16;
}
#[inline]
fn overflow(&mut self) {
// reload counter
self.data = self.initial_data;
if self.ctl.irq_enabled() {
interrupt::signal_irq(&self.interrupt_flags, self.irq);
}
}
/// increments the timer with an amount of ticks
/// returns the number of times it overflowed
fn update(&mut self, ticks: usize) -> usize {
let mut ticks = ticks as u32;
let mut num_overflows = 0;
let ticks_remaining = self.ticks_to_overflow();
if ticks >= ticks_remaining {
num_overflows += 1;
ticks -= ticks_remaining;
self.data = self.initial_data;
let ticks_remaining = self.ticks_to_overflow();
num_overflows += ticks / ticks_remaining;
ticks = ticks % ticks_remaining;
if self.ctl.irq_enabled() {
interrupt::signal_irq(&self.interrupt_flags, self.irq);
}
}
self.data += ticks as u16;
num_overflows as usize
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Timers {
#[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
timers: [Timer; 4],
running_timers: u8,
pub trace: bool,
}
impl InterruptConnect for Timers {
fn connect_irq(&mut self, interrupt_flags: SharedInterruptFlags) {
for timer in &mut self.timers {
timer.interrupt_flags = interrupt_flags.clone();
}
}
}
impl std::ops::Index<usize> for Timers {
type Output = Timer;
fn index(&self, index: usize) -> &Self::Output {
&self.timers[index]
}
}
impl std::ops::IndexMut<usize> for Timers {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.timers[index]
}
}
impl Timers {
pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> Timers {
Timers {
scheduler,
timers: [
Timer::new(0, interrupt_flags.clone()),
Timer::new(1, interrupt_flags.clone()),
Timer::new(2, interrupt_flags.clone()),
Timer::new(3, interrupt_flags.clone()),
],
running_timers: 0,
trace: false,
}
}
fn add_timer_event(&mut self, id: usize) {
let timer = &mut self.timers[id];
timer.is_scheduled = true;
timer.start_time = self.scheduler.timestamp();
let cycles = (timer.ticks_to_overflow() as usize) << timer.prescalar_shift;
self.scheduler
.push(EventType::TimerOverflow(id), cycles);
}
fn cancel_timer_event(&mut self, id: usize) {
self.scheduler.cancel(EventType::TimerOverflow(id));
self[id].is_scheduled = false;
}
fn handle_timer_overflow(
&mut self,
id: usize,
apu: &mut SoundController,
dmac: &mut DmaController,
) {
self[id].overflow();
if id != 3 {
let next_timer_id = id + 1;
let next_timer = &mut self.timers[next_timer_id];
if next_timer.ctl.cascade() {
if next_timer.update(1) > 0 {
drop(next_timer);
self.handle_timer_overflow(next_timer_id, apu, dmac);
}
}
}
if id == 0 || id == 1 {
apu.handle_timer_overflow(dmac, id, 1);
}
}
pub fn handle_overflow_event(
&mut self,
id: usize,
extra_cycles: usize,
apu: &mut SoundController,
dmac: &mut DmaController,
) {
self.handle_timer_overflow(id, apu, dmac);
// TODO: re-use add_timer_event function
let timer = &mut self.timers[id];
timer.is_scheduled = true;
timer.start_time = self.scheduler.timestamp() - extra_cycles;
let cycles = (timer.ticks_to_overflow() as usize) << timer.prescalar_shift;
self.scheduler
.push(EventType::TimerOverflow(id), cycles - extra_cycles);
}
pub fn write_timer_ctl(&mut self, id: usize, value: u16) {
let timer = &mut self.timers[id];
let new_ctl = TimerCtl(value);
let old_enabled = timer.ctl.enabled();
let new_enabled = new_ctl.enabled();
let cascade = new_ctl.cascade();
timer.prescalar_shift = SHIFT_LUT[new_ctl.prescalar() as usize];
timer.ctl = new_ctl;
if new_enabled && !cascade {
self.running_timers |= 1 << id;
self.cancel_timer_event(id);
self.add_timer_event(id);
} else {
self.running_timers &= !(1 << id);
self.cancel_timer_event(id);
}
if old_enabled != new_enabled {
trace!(
"TMR{} {}",
id,
if new_enabled { "enabled" } else { "disabled" }
);
}
}
#[inline]
fn read_timer_data(&mut self, id: usize) -> u16 {
let timer = &mut self.timers[id];
if timer.is_scheduled {
// this timer is controlled by the scheduler so we need to manually calculate
// the current value of the counter
timer.sync_timer_data(self.scheduler.timestamp());
}
timer.data
}
pub fn handle_read(&mut self, io_addr: u32) -> u16 {
match io_addr {
REG_TM0CNT_H => self.timers[0].ctl.0,
REG_TM1CNT_H => self.timers[1].ctl.0,
REG_TM2CNT_H => self.timers[2].ctl.0,
REG_TM3CNT_H => self.timers[3].ctl.0,
REG_TM0CNT_L => self.read_timer_data(0),
REG_TM1CNT_L => self.read_timer_data(1),
REG_TM2CNT_L => self.read_timer_data(2),
REG_TM3CNT_L => self.read_timer_data(3),
_ => unreachable!(),
}
}
pub fn handle_write(&mut self, io_addr: u32, value: u16) {
match io_addr {
REG_TM0CNT_L => {
self.timers[0].data = value;
self.timers[0].initial_data = value;
}
REG_TM0CNT_H => self.write_timer_ctl(0, value),
REG_TM1CNT_L => {
self.timers[1].data = value;
self.timers[1].initial_data = value;
}
REG_TM1CNT_H => self.write_timer_ctl(1, value),
REG_TM2CNT_L => {
self.timers[2].data = value;
self.timers[2].initial_data = value;
}
REG_TM2CNT_H => self.write_timer_ctl(2, value),
REG_TM3CNT_L => {
self.timers[3].data = value;
self.timers[3].initial_data = value;
}
REG_TM3CNT_H => self.write_timer_ctl(3, value),
_ => unreachable!(),
}
}
}
bitfield! {
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct TimerCtl(u16);
impl Debug;
u16;
prescalar, _ : 1, 0;
cascade, _ : 2;
irq_enabled, _ : 6;
enabled, set_enabled : 7;
}