diff --git a/core/src/cartridge/backup/eeprom.rs b/core/src/cartridge/backup/eeprom.rs index ff74075..f590c5a 100644 --- a/core/src/cartridge/backup/eeprom.rs +++ b/core/src/cartridge/backup/eeprom.rs @@ -254,7 +254,7 @@ impl EepromChip { result } - pub(in crate) fn is_transmitting(&self) -> bool { + pub(crate) fn is_transmitting(&self) -> bool { use SpiState::*; match self.state { TxData | TxDummy => true, @@ -262,7 +262,7 @@ impl EepromChip { } } - pub(in crate) fn reset(&mut self) { + pub(crate) fn reset(&mut self) { self.state = SpiState::RxInstruction; self.reset_rx_buffer(); self.reset_tx_buffer(); @@ -280,7 +280,7 @@ impl EepromChip { /// Eeprom controller can programmed with DMA accesses in 16bit mode #[derive(Serialize, Deserialize, Clone, Debug)] pub struct EepromController { - pub(in crate) chip: RefCell, + pub(crate) chip: RefCell, detect: bool, } diff --git a/core/src/cartridge/gpio.rs b/core/src/cartridge/gpio.rs index 724da2a..fba4cc0 100644 --- a/core/src/cartridge/gpio.rs +++ b/core/src/cartridge/gpio.rs @@ -27,7 +27,7 @@ pub trait GpioDevice: Sized { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Gpio { - pub(in crate) rtc: Option, + pub(crate) rtc: Option, direction: GpioState, control: GpioPortControl, } diff --git a/core/src/cartridge/mod.rs b/core/src/cartridge/mod.rs index 54d4dba..e2062a7 100644 --- a/core/src/cartridge/mod.rs +++ b/core/src/cartridge/mod.rs @@ -44,7 +44,7 @@ pub struct Cartridge { size: usize, gpio: Option, symbols: Option, // TODO move it somewhere else - pub(in crate) backup: BackupMedia, + pub(crate) backup: BackupMedia, } impl Cartridge { diff --git a/core/src/dma.rs b/core/src/dma.rs index f4605a8..e32f282 100644 --- a/core/src/dma.rs +++ b/core/src/dma.rs @@ -2,7 +2,7 @@ use super::arm7tdmi::memory::{MemoryAccess, MemoryInterface}; use super::cartridge::BackupMedia; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B}; -use super::sched::{EventType, Scheduler, SchedulerConnect, SharedScheduler}; +use super::sched::{EventType, Scheduler}; use super::sysbus::SysBus; use num::FromPrimitive; @@ -118,7 +118,7 @@ impl DmaChannel { return start_immediately; } - fn xfer(&mut self, sb: &mut SysBus) { + fn transfer(&mut self, sb: &mut SysBus) { let word_size = if self.ctrl.is_32bit() { 4 } else { 2 }; let count = match self.internal.count { 0 => match self.id { @@ -197,9 +197,6 @@ impl DmaChannel { pub struct DmaController { pub channels: [DmaChannel; 4], pending_set: u8, - #[serde(skip)] - #[serde(default = "Scheduler::new_shared")] - scheduler: SharedScheduler, #[cfg(feature = "debugger")] pub trace: bool, } @@ -212,14 +209,8 @@ impl InterruptConnect for DmaController { } } -impl SchedulerConnect for DmaController { - fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.scheduler = scheduler; - } -} - impl DmaController { - pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> DmaController { + pub fn new(interrupt_flags: SharedInterruptFlags) -> DmaController { DmaController { channels: [ DmaChannel::new(0, interrupt_flags.clone()), @@ -228,8 +219,6 @@ impl DmaController { DmaChannel::new(3, interrupt_flags.clone()), ], pending_set: 0, - scheduler: scheduler, - #[cfg(feature = "debugger")] trace: false, } @@ -242,13 +231,13 @@ impl DmaController { pub fn perform_work(&mut self, sb: &mut SysBus) { for id in 0..4 { if self.pending_set & (1 << id) != 0 { - self.channels[id].xfer(sb); + self.channels[id].transfer(sb); } } self.pending_set = 0; } - pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) { + pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16, sched: &mut Scheduler) { match ofs { 0 => self.channels[channel_id].write_src_low(value), 2 => self.channels[channel_id].write_src_high(value), @@ -262,8 +251,7 @@ impl DmaController { let start_immediately = self.channels[channel_id].write_dma_ctrl(value); if start_immediately { // DMA actually starts after 3 cycles - self.scheduler - .push(EventType::DmaActivateChannel(channel_id), 3); + sched.schedule((EventType::DmaActivateChannel(channel_id), 3)); } else { self.deactivate_channel(channel_id); } diff --git a/core/src/gba.rs b/core/src/gba.rs index 2245a20..d46291c 100644 --- a/core/src/gba.rs +++ b/core/src/gba.rs @@ -78,17 +78,24 @@ impl GameBoyAdvance { }; let interrupt_flags = Rc::new(Cell::new(IrqBitmask(0))); - let scheduler = Scheduler::new_shared(); + let mut scheduler = Scheduler::new_shared(); let intc = InterruptController::new(interrupt_flags.clone()); - let gpu = Box::new(Gpu::new(scheduler.clone(), interrupt_flags.clone())); - let dmac = DmaController::new(interrupt_flags.clone(), scheduler.clone()); - let timers = Timers::new(interrupt_flags.clone(), scheduler.clone()); + let gpu = Box::new(Gpu::new(&mut scheduler, interrupt_flags.clone())); + let dmac = DmaController::new(interrupt_flags.clone()); + let timers = Timers::new(interrupt_flags.clone()); let sound_controller = Box::new(SoundController::new( - scheduler.clone(), + &mut scheduler, audio_device.borrow().get_sample_rate() as f32, )); - let io_devs = Shared::new(IoDevices::new(intc, gpu, dmac, timers, sound_controller)); + let io_devs = Shared::new(IoDevices::new( + intc, + gpu, + dmac, + timers, + sound_controller, + scheduler.clone(), + )); let sysbus = Shared::new(SysBus::new( scheduler.clone(), io_devs.clone(), @@ -193,7 +200,6 @@ impl GameBoyAdvance { self.sysbus.set_ewram(decoded.ewram); // Redistribute shared pointers self.io_devs.connect_irq(self.interrupt_flags.clone()); - self.io_devs.connect_scheduler(self.scheduler.clone()); self.sysbus.connect_scheduler(self.scheduler.clone()); self.sysbus.set_io_devices(self.io_devs.clone()); self.sysbus.cartridge.update_from(decoded.cartridge); @@ -254,7 +260,7 @@ impl GameBoyAdvance { // Register an event to mark the end of this run self.scheduler - .push(EventType::RunLimitReached, cycles_to_run); + .schedule_at(EventType::RunLimitReached, run_start_time + cycles_to_run); let mut running = true; while running { @@ -274,47 +280,52 @@ impl GameBoyAdvance { self.io_devs.haltcnt = HaltState::Running; } else { self.scheduler.fast_forward_to_next(); - let (event, cycles_late) = self - .scheduler - .pop_pending_event() - .unwrap_or_else(|| unreachable!()); - self.handle_event(event, cycles_late, &mut running); + self.handle_events(&mut running) } } } } - while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() { - self.handle_event(event, cycles_late, &mut running); - } + self.handle_events(&mut running); } let total_cycles_ran = self.scheduler.timestamp() - run_start_time; total_cycles_ran - cycles_to_run } - #[inline] - fn handle_event(&mut self, event: EventType, cycles_late: usize, running: &mut bool) { + fn handle_events(&mut self, run_limit_flag: &mut bool) { let io = &mut (*self.io_devs); - match event { - EventType::RunLimitReached => { - *running = false; + while let Some((event, event_time)) = self.scheduler.pop_pending_event() { + // Since we only examine the scheduler queue every so often, most events will be handled late by a few cycles. + // We sacrifice accuricy in favor of performance, otherwise we would have to check the event queue + // every cpu cycle, where in 99% of cases it will always be empty. + let new_event = match event { + EventType::RunLimitReached => { + *run_limit_flag = false; + None + } + EventType::DmaActivateChannel(channel_id) => { + io.dmac.activate_channel(channel_id); + None + } + EventType::TimerOverflow(channel_id) => { + let timers = &mut io.timers; + let dmac = &mut io.dmac; + let apu = &mut io.sound; + Some(timers.handle_overflow_event(channel_id, event_time, apu, dmac)) + } + EventType::Gpu(gpu_event) => Some(io.gpu.on_event( + gpu_event, + &mut *self.sysbus, + #[cfg(not(feature = "no_video_interface"))] + &self.video_device, + )), + EventType::Apu(event) => Some(io.sound.on_event(event, &self.audio_device)), + }; + if let Some((new_event, when)) = new_event { + // We schedule events added by event handlers relative to the handled event time + self.scheduler.schedule_at(new_event, event_time + when) } - EventType::DmaActivateChannel(channel_id) => io.dmac.activate_channel(channel_id), - EventType::TimerOverflow(channel_id) => { - let timers = &mut io.timers; - let dmac = &mut io.dmac; - let apu = &mut io.sound; - timers.handle_overflow_event(channel_id, cycles_late, apu, dmac); - } - EventType::Gpu(event) => io.gpu.on_event( - event, - cycles_late, - &mut *self.sysbus, - #[cfg(not(feature = "no_video_interface"))] - &self.video_device, - ), - EventType::Apu(event) => io.sound.on_event(event, cycles_late, &self.audio_device), } } diff --git a/core/src/gpu/mod.rs b/core/src/gpu/mod.rs index ba65ff3..1110695 100644 --- a/core/src/gpu/mod.rs +++ b/core/src/gpu/mod.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use super::bus::*; use super::dma::{DmaNotifer, TIMING_HBLANK, TIMING_VBLANK}; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; -use super::sched::*; +use super::sched::{EventType, FutureEvent, GpuEvent, Scheduler}; pub use super::sysbus::consts::*; #[cfg(not(feature = "no_video_interface"))] use super::VideoInterface; @@ -113,11 +113,6 @@ type VideoDeviceRcRefCell = Rc>; pub struct Gpu { interrupt_flags: SharedInterruptFlags, - /// When deserializing this struct using serde, make sure to call connect_scheduler - #[serde(skip)] - #[serde(default = "Scheduler::new_shared")] - scheduler: SharedScheduler, - /// how many cycles left until next gpu state ? cycles_left_for_current_state: usize, @@ -153,15 +148,11 @@ impl InterruptConnect for Gpu { } } -impl SchedulerConnect for Gpu { - fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.scheduler = scheduler; - } -} +type FutureGpuEvent = (GpuEvent, usize); impl Gpu { - pub fn new(mut scheduler: SharedScheduler, interrupt_flags: SharedInterruptFlags) -> Gpu { - scheduler.push_gpu_event(GpuEvent::HDraw, CYCLES_HDRAW); + pub fn new(sched: &mut Scheduler, interrupt_flags: SharedInterruptFlags) -> Gpu { + sched.schedule((EventType::Gpu(GpuEvent::HDraw), CYCLES_HDRAW)); fn alloc_scanline_buffer() -> Box<[Rgb15]> { vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH].into_boxed_slice() @@ -169,7 +160,6 @@ impl Gpu { Gpu { interrupt_flags, - scheduler, dispcnt: DisplayControl::from(0x80), dispstat: Default::default(), bgcnt: Default::default(), @@ -367,7 +357,7 @@ impl Gpu { } #[inline] - fn handle_hdraw_end(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) { + fn handle_hdraw_end(&mut self, dma_notifier: &mut D) -> FutureGpuEvent { self.dispstat.hblank_flag = true; if self.dispstat.hblank_irq_enable { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); @@ -382,7 +372,7 @@ impl Gpu { &mut self, dma_notifier: &mut D, #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) -> (GpuEvent, usize) { + ) -> FutureGpuEvent { self.update_vcount(self.vcount + 1); if self.vcount < DISPLAY_HEIGHT { @@ -419,7 +409,7 @@ impl Gpu { } } - fn handle_vblank_hdraw_end(&mut self) -> (GpuEvent, usize) { + fn handle_vblank_hdraw_end(&mut self) -> FutureGpuEvent { self.dispstat.hblank_flag = true; if self.dispstat.hblank_irq_enable { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); @@ -427,7 +417,7 @@ impl Gpu { (GpuEvent::VBlankHBlank, CYCLES_HBLANK) } - fn handle_vblank_hblank_end(&mut self) -> (GpuEvent, usize) { + fn handle_vblank_hblank_end(&mut self) -> FutureGpuEvent { if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 { self.update_vcount(self.vcount + 1); self.dispstat.hblank_flag = false; @@ -444,13 +434,13 @@ impl Gpu { pub fn on_event( &mut self, event: GpuEvent, - extra_cycles: usize, dma_notifier: &mut D, #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) where + ) -> FutureEvent + where D: DmaNotifer, { - let (next_event, cycles) = match event { + let (event, when) = match event { GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier), GpuEvent::HBlank => self.handle_hblank_end( dma_notifier, @@ -460,8 +450,7 @@ impl Gpu { GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(), GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(), }; - self.scheduler - .push(EventType::Gpu(next_event), cycles - extra_cycles); + (EventType::Gpu(event), when) } } @@ -573,8 +562,8 @@ mod tests { #[test] fn test_gpu_state_machine() { - let mut sched = Scheduler::new_shared(); - let mut gpu = Gpu::new(sched.clone(), Rc::new(Cell::new(Default::default()))); + let mut sched = Scheduler::new(); + let mut gpu = Gpu::new(&mut sched, Rc::new(Cell::new(Default::default()))); #[cfg(not(feature = "no_video_interface"))] let video = Rc::new(RefCell::new(TestVideoInterface::default())); #[cfg(not(feature = "no_video_interface"))] diff --git a/core/src/iodev.rs b/core/src/iodev.rs index e482601..b482783 100644 --- a/core/src/iodev.rs +++ b/core/src/iodev.rs @@ -8,7 +8,7 @@ use super::gpu::*; use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags}; use super::keypad; use super::mgba_debug::DebugPort; -use super::sched::{SchedulerConnect, SharedScheduler}; +use super::sched::{Scheduler, SchedulerConnect, SharedScheduler}; use super::sound::SoundController; use super::sysbus::SysBusPtr; use super::timer::Timers; @@ -40,6 +40,9 @@ pub struct IoDevices { // HACK // my ownership design sucks #[serde(skip)] + #[serde(default = "Scheduler::new_shared")] + scheduler: SharedScheduler, + #[serde(skip)] #[serde(default = "SysBusPtr::default")] sysbus_ptr: SysBusPtr, } @@ -51,6 +54,7 @@ impl IoDevices { dmac: DmaController, timers: Timers, sound_controller: Box, + scheduler: SharedScheduler, ) -> IoDevices { IoDevices { intc, @@ -63,7 +67,7 @@ impl IoDevices { keyinput: keypad::KEYINPUT_ALL_RELEASED, waitcnt: WaitControl(0), debug: DebugPort::new(), - + scheduler: scheduler, sysbus_ptr: Default::default(), } } @@ -84,10 +88,7 @@ impl InterruptConnect for IoDevices { impl SchedulerConnect for IoDevices { fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.gpu.connect_scheduler(scheduler.clone()); - self.sound.connect_scheduler(scheduler.clone()); - self.dmac.connect_scheduler(scheduler.clone()); - self.timers.connect_scheduler(scheduler.clone()); + self.scheduler = scheduler.clone(); } } @@ -124,7 +125,7 @@ impl Bus for IoDevices { REG_IE => io.intc.interrupt_enable.0 as u16, REG_IF => io.intc.interrupt_flags.get().value() as u16, - REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_read(io_addr), + REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_read(io_addr, &io.scheduler), SOUND_BASE..=SOUND_END => io.sound.handle_read(io_addr), REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0, @@ -263,7 +264,9 @@ impl Bus for IoDevices { REG_IE => io.intc.interrupt_enable.0 = value, REG_IF => io.intc.clear(value), - REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_write(io_addr, value), + REG_TM0CNT_L..=REG_TM3CNT_H => { + io.timers.handle_write(io_addr, value, &mut io.scheduler) + } SOUND_BASE..=SOUND_END => { io.sound.handle_write(io_addr, value); @@ -272,7 +275,8 @@ impl Bus for IoDevices { DMA_BASE..=REG_DMA3CNT_H => { let ofs = io_addr - DMA_BASE; let channel_id = (ofs / 12) as usize; - io.dmac.write_16(channel_id, ofs % 12, value) + io.dmac + .write_16(channel_id, ofs % 12, value, &mut io.scheduler) } REG_WAITCNT => { diff --git a/core/src/sched.rs b/core/src/sched.rs index 0221ab4..3464a3e 100644 --- a/core/src/sched.rs +++ b/core/src/sched.rs @@ -1,4 +1,3 @@ -use std::cell::Cell; use std::cmp::Ordering; use std::collections::BinaryHeap; @@ -42,28 +41,22 @@ pub struct Event { typ: EventType, /// Timestamp in cycles time: usize, - cancel: Cell, } impl Event { - fn new(typ: EventType, time: usize) -> Event { - Event { - typ, - time, - cancel: Cell::new(false), - } + pub fn new(typ: EventType, time: usize) -> Event { + Event { typ, time } } #[inline] fn get_type(&self) -> EventType { self.typ } - - fn is_canceled(&self) -> bool { - self.cancel.get() - } } +/// Future event is an event to be scheduled in x cycles from now +pub type FutureEvent = (EventType, usize); + impl Ord for Event { fn cmp(&self, other: &Self) -> Ordering { self.time.cmp(&other.time).reverse() @@ -140,27 +133,27 @@ impl Scheduler { self.events.peek().map(|e| e.typ) } - /// Schedule an event to be executed in `cycles` cycles from now - pub fn push(&mut self, typ: EventType, cycles: usize) { - let event = Event::new(typ, self.timestamp + cycles); + /// Schedule an event to be executed in `when` cycles from now + pub fn schedule(&mut self, event: FutureEvent) { + let (typ, when) = event; + let event = Event::new(typ, self.timestamp + when); self.events.push(event); } + /// Schedule an event to be executed at an exact timestamp, can be used to schedule "past" events. + pub fn schedule_at(&mut self, event_typ: EventType, timestamp: usize) { + self.events.push(Event::new(event_typ, timestamp)); + } + /// Cancel all events with type `typ` - /// This method is rather expansive to call - pub fn cancel(&mut self, typ: EventType) { + /// This method is rather expansive to call since we are reallocating the entire event tree + pub fn cancel_pending(&mut self, typ: EventType) { + let mut new_events = BinaryHeap::with_capacity(NUM_EVENTS); self.events .iter() - .filter(|e| e.typ == typ) - .for_each(|e| e.cancel.set(true)); - } - - pub fn push_gpu_event(&mut self, e: GpuEvent, cycles: usize) { - self.push(EventType::Gpu(e), cycles); - } - - pub fn push_apu_event(&mut self, e: ApuEvent, cycles: usize) { - self.push(EventType::Apu(e), cycles); + .filter(|e| e.typ != typ) + .for_each(|e| new_events.push(e.clone())); + self.events = new_events; } /// Updates the scheduler timestamp @@ -174,11 +167,7 @@ impl Scheduler { if self.timestamp >= event.time { // remove the event let event = self.events.pop().unwrap_or_else(|| unreachable!()); - if !event.is_canceled() { - Some((event.get_type(), self.timestamp - event.time)) - } else { - None - } + Some((event.get_type(), event.time)) } else { None } diff --git a/core/src/sound/mod.rs b/core/src/sound/mod.rs index 576e0bb..feffbaa 100644 --- a/core/src/sound/mod.rs +++ b/core/src/sound/mod.rs @@ -63,10 +63,6 @@ type AudioDeviceRcRefCell = Rc>; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SoundController { - #[serde(skip)] - #[serde(default = "Scheduler::new_shared")] - scheduler: SharedScheduler, - cycles: usize, // cycles count when we last provided a new sample. mse: bool, @@ -105,19 +101,12 @@ pub struct SoundController { output_buffer: Vec>, } -impl SchedulerConnect for SoundController { - fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.scheduler = scheduler; - } -} - impl SoundController { - pub fn new(mut scheduler: SharedScheduler, audio_device_sample_rate: f32) -> SoundController { + pub fn new(sched: &mut Scheduler, audio_device_sample_rate: f32) -> SoundController { let resampler = CosineResampler::new(32768_f32, audio_device_sample_rate); let cycles_per_sample = 512; - scheduler.push(EventType::Apu(ApuEvent::Sample), cycles_per_sample); + sched.schedule((EventType::Apu(ApuEvent::Sample), cycles_per_sample)); SoundController { - scheduler, cycles_per_sample, cycles: 0, mse: false, @@ -334,7 +323,7 @@ impl SoundController { } #[inline] - fn on_sample(&mut self, extra_cycles: usize, audio_device: &AudioDeviceRcRefCell) { + fn on_sample(&mut self, audio_device: &AudioDeviceRcRefCell) -> FutureEvent { let mut sample = [0f32; 2]; for channel in 0..=1 { @@ -360,20 +349,17 @@ impl SoundController { (right.round() as i16) * (std::i16::MAX / 512), ]); }); - - self.scheduler - .push_apu_event(ApuEvent::Sample, self.cycles_per_sample - extra_cycles); + (EventType::Apu(ApuEvent::Sample), self.cycles_per_sample) } pub fn on_event( &mut self, event: ApuEvent, - extra_cycles: usize, audio_device: &AudioDeviceRcRefCell, - ) { + ) -> FutureEvent { match event { - ApuEvent::Sample => self.on_sample(extra_cycles, audio_device), - _ => debug!("got {:?} event", event), + ApuEvent::Sample => self.on_sample(audio_device), + _ => unimplemented!("got {:?} event", event), } } } diff --git a/core/src/sysbus.rs b/core/src/sysbus.rs index a78bd67..f0c3b6b 100644 --- a/core/src/sysbus.rs +++ b/core/src/sysbus.rs @@ -161,7 +161,8 @@ pub type SysBusPtr = WeakPointer; impl SchedulerConnect for SysBus { fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.scheduler = scheduler; + self.scheduler = scheduler.clone(); + self.io.connect_scheduler(scheduler.clone()); } } diff --git a/core/src/timer.rs b/core/src/timer.rs index e3714af..d474d57 100644 --- a/core/src/timer.rs +++ b/core/src/timer.rs @@ -1,7 +1,7 @@ use super::dma::DmaController; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::iodev::consts::*; -use super::sched::{EventType, Scheduler, SchedulerConnect, SharedScheduler}; +use super::sched::{EventType, FutureEvent, Scheduler}; use super::sound::SoundController; use num::FromPrimitive; @@ -93,9 +93,6 @@ impl Timer { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Timers { - #[serde(skip)] - #[serde(default = "Scheduler::new_shared")] - scheduler: SharedScheduler, timers: [Timer; 4], running_timers: u8, @@ -111,12 +108,6 @@ impl InterruptConnect for Timers { } } -impl SchedulerConnect for Timers { - fn connect_scheduler(&mut self, scheduler: SharedScheduler) { - self.scheduler = scheduler; - } -} - impl std::ops::Index for Timers { type Output = Timer; fn index(&self, index: usize) -> &Self::Output { @@ -131,9 +122,8 @@ impl std::ops::IndexMut for Timers { } impl Timers { - pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> Timers { + pub fn new(interrupt_flags: SharedInterruptFlags) -> Timers { Timers { - scheduler, timers: [ Timer::new(0, interrupt_flags.clone()), Timer::new(1, interrupt_flags.clone()), @@ -147,16 +137,16 @@ impl Timers { } } - fn add_timer_event(&mut self, id: usize) { + fn prepare_next_overflow_event(&mut self, id: usize, start_time: usize) -> FutureEvent { let timer = &mut self.timers[id]; timer.is_scheduled = true; - timer.start_time = self.scheduler.timestamp(); + timer.start_time = start_time; let cycles = (timer.ticks_to_overflow() as usize) << timer.prescalar_shift; - self.scheduler.push(EventType::TimerOverflow(id), cycles); + (EventType::TimerOverflow(id), cycles) } - fn cancel_timer_event(&mut self, id: usize) { - self.scheduler.cancel(EventType::TimerOverflow(id)); + fn cancel_timer_event(&mut self, id: usize, sched: &mut Scheduler) { + sched.cancel_pending(EventType::TimerOverflow(id)); self[id].is_scheduled = false; } @@ -185,22 +175,15 @@ impl Timers { pub fn handle_overflow_event( &mut self, id: usize, - extra_cycles: usize, + overflow_time: usize, apu: &mut SoundController, dmac: &mut DmaController, - ) { + ) -> FutureEvent { 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); + self.prepare_next_overflow_event(id, overflow_time) } - pub fn write_timer_ctl(&mut self, id: usize, value: u16) { + pub fn write_timer_ctl(&mut self, id: usize, value: u16, sched: &mut Scheduler) { let timer = &mut self.timers[id]; let new_ctl = TimerCtl(value); #[cfg(feature = "debugger")] @@ -211,11 +194,11 @@ impl Timers { timer.ctl = new_ctl; if new_enabled && !cascade { self.running_timers |= 1 << id; - self.cancel_timer_event(id); - self.add_timer_event(id); + self.cancel_timer_event(id, sched); + sched.schedule(self.prepare_next_overflow_event(id, sched.timestamp())); } else { self.running_timers &= !(1 << id); - self.cancel_timer_event(id); + self.cancel_timer_event(id, sched); } #[cfg(feature = "debugger")] @@ -231,56 +214,56 @@ impl Timers { } #[inline] - fn read_timer_data(&mut self, id: usize) -> u16 { + fn read_timer_data(&mut self, id: usize, sched: &Scheduler) -> u16 { let timer = &mut self.timers[id]; if timer.is_scheduled { - // this timer is controlled by the scheduler so we need to manually calculate + // this timer is controlled by the sched so we need to manually calculate // the current value of the counter - timer.sync_timer_data(self.scheduler.timestamp()); + timer.sync_timer_data(sched.timestamp()); } timer.data } - pub fn handle_read(&mut self, io_addr: u32) -> u16 { + pub fn handle_read(&mut self, io_addr: u32, sched: &Scheduler) -> 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), + REG_TM0CNT_L => self.read_timer_data(0, sched), + REG_TM1CNT_L => self.read_timer_data(1, sched), + REG_TM2CNT_L => self.read_timer_data(2, sched), + REG_TM3CNT_L => self.read_timer_data(3, sched), _ => unreachable!(), } } - pub fn handle_write(&mut self, io_addr: u32, value: u16) { + pub fn handle_write(&mut self, io_addr: u32, value: u16, sched: &mut Scheduler) { 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_TM0CNT_H => self.write_timer_ctl(0, value, sched), REG_TM1CNT_L => { self.timers[1].data = value; self.timers[1].initial_data = value; } - REG_TM1CNT_H => self.write_timer_ctl(1, value), + REG_TM1CNT_H => self.write_timer_ctl(1, value, sched), REG_TM2CNT_L => { self.timers[2].data = value; self.timers[2].initial_data = value; } - REG_TM2CNT_H => self.write_timer_ctl(2, value), + REG_TM2CNT_H => self.write_timer_ctl(2, value, sched), REG_TM3CNT_L => { self.timers[3].data = value; self.timers[3].initial_data = value; } - REG_TM3CNT_H => self.write_timer_ctl(3, value), + REG_TM3CNT_H => self.write_timer_ctl(3, value, sched), _ => unreachable!(), } }