Refactor&improve the scheduler to handle missed events

Solves #168


Former-commit-id: 09104a6ebefdaf2e33ae22eb08860d9711ecb66b
Former-commit-id: e07219e40fa187f516dd266ab753a67f3c7e796b
This commit is contained in:
Michel Heily 2022-09-04 02:49:04 +03:00
parent 7af9bc5760
commit 91453e8777
11 changed files with 142 additions and 191 deletions

View file

@ -254,7 +254,7 @@ impl EepromChip {
result result
} }
pub(in crate) fn is_transmitting(&self) -> bool { pub(crate) fn is_transmitting(&self) -> bool {
use SpiState::*; use SpiState::*;
match self.state { match self.state {
TxData | TxDummy => true, 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.state = SpiState::RxInstruction;
self.reset_rx_buffer(); self.reset_rx_buffer();
self.reset_tx_buffer(); self.reset_tx_buffer();
@ -280,7 +280,7 @@ impl EepromChip {
/// Eeprom controller can programmed with DMA accesses in 16bit mode /// Eeprom controller can programmed with DMA accesses in 16bit mode
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct EepromController { pub struct EepromController {
pub(in crate) chip: RefCell<EepromChip>, pub(crate) chip: RefCell<EepromChip>,
detect: bool, detect: bool,
} }

View file

@ -27,7 +27,7 @@ pub trait GpioDevice: Sized {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Gpio { pub struct Gpio {
pub(in crate) rtc: Option<Rtc>, pub(crate) rtc: Option<Rtc>,
direction: GpioState, direction: GpioState,
control: GpioPortControl, control: GpioPortControl,
} }

View file

@ -44,7 +44,7 @@ pub struct Cartridge {
size: usize, size: usize,
gpio: Option<Gpio>, gpio: Option<Gpio>,
symbols: Option<SymbolTable>, // TODO move it somewhere else symbols: Option<SymbolTable>, // TODO move it somewhere else
pub(in crate) backup: BackupMedia, pub(crate) backup: BackupMedia,
} }
impl Cartridge { impl Cartridge {

View file

@ -2,7 +2,7 @@ use super::arm7tdmi::memory::{MemoryAccess, MemoryInterface};
use super::cartridge::BackupMedia; use super::cartridge::BackupMedia;
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B}; 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 super::sysbus::SysBus;
use num::FromPrimitive; use num::FromPrimitive;
@ -118,7 +118,7 @@ impl DmaChannel {
return start_immediately; 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 word_size = if self.ctrl.is_32bit() { 4 } else { 2 };
let count = match self.internal.count { let count = match self.internal.count {
0 => match self.id { 0 => match self.id {
@ -197,9 +197,6 @@ impl DmaChannel {
pub struct DmaController { pub struct DmaController {
pub channels: [DmaChannel; 4], pub channels: [DmaChannel; 4],
pending_set: u8, pending_set: u8,
#[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
pub trace: bool, 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 { impl DmaController {
pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> DmaController { pub fn new(interrupt_flags: SharedInterruptFlags) -> DmaController {
DmaController { DmaController {
channels: [ channels: [
DmaChannel::new(0, interrupt_flags.clone()), DmaChannel::new(0, interrupt_flags.clone()),
@ -228,8 +219,6 @@ impl DmaController {
DmaChannel::new(3, interrupt_flags.clone()), DmaChannel::new(3, interrupt_flags.clone()),
], ],
pending_set: 0, pending_set: 0,
scheduler: scheduler,
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
trace: false, trace: false,
} }
@ -242,13 +231,13 @@ impl DmaController {
pub fn perform_work(&mut self, sb: &mut SysBus) { pub fn perform_work(&mut self, sb: &mut SysBus) {
for id in 0..4 { for id in 0..4 {
if self.pending_set & (1 << id) != 0 { if self.pending_set & (1 << id) != 0 {
self.channels[id].xfer(sb); self.channels[id].transfer(sb);
} }
} }
self.pending_set = 0; 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 { match ofs {
0 => self.channels[channel_id].write_src_low(value), 0 => self.channels[channel_id].write_src_low(value),
2 => self.channels[channel_id].write_src_high(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); let start_immediately = self.channels[channel_id].write_dma_ctrl(value);
if start_immediately { if start_immediately {
// DMA actually starts after 3 cycles // DMA actually starts after 3 cycles
self.scheduler sched.schedule((EventType::DmaActivateChannel(channel_id), 3));
.push(EventType::DmaActivateChannel(channel_id), 3);
} else { } else {
self.deactivate_channel(channel_id); self.deactivate_channel(channel_id);
} }

View file

@ -78,17 +78,24 @@ impl GameBoyAdvance {
}; };
let interrupt_flags = Rc::new(Cell::new(IrqBitmask(0))); 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 intc = InterruptController::new(interrupt_flags.clone());
let gpu = Box::new(Gpu::new(scheduler.clone(), interrupt_flags.clone())); let gpu = Box::new(Gpu::new(&mut scheduler, interrupt_flags.clone()));
let dmac = DmaController::new(interrupt_flags.clone(), scheduler.clone()); let dmac = DmaController::new(interrupt_flags.clone());
let timers = Timers::new(interrupt_flags.clone(), scheduler.clone()); let timers = Timers::new(interrupt_flags.clone());
let sound_controller = Box::new(SoundController::new( let sound_controller = Box::new(SoundController::new(
scheduler.clone(), &mut scheduler,
audio_device.borrow().get_sample_rate() as f32, 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( let sysbus = Shared::new(SysBus::new(
scheduler.clone(), scheduler.clone(),
io_devs.clone(), io_devs.clone(),
@ -193,7 +200,6 @@ impl GameBoyAdvance {
self.sysbus.set_ewram(decoded.ewram); self.sysbus.set_ewram(decoded.ewram);
// Redistribute shared pointers // Redistribute shared pointers
self.io_devs.connect_irq(self.interrupt_flags.clone()); 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.connect_scheduler(self.scheduler.clone());
self.sysbus.set_io_devices(self.io_devs.clone()); self.sysbus.set_io_devices(self.io_devs.clone());
self.sysbus.cartridge.update_from(decoded.cartridge); self.sysbus.cartridge.update_from(decoded.cartridge);
@ -254,7 +260,7 @@ impl GameBoyAdvance {
// Register an event to mark the end of this run // Register an event to mark the end of this run
self.scheduler self.scheduler
.push(EventType::RunLimitReached, cycles_to_run); .schedule_at(EventType::RunLimitReached, run_start_time + cycles_to_run);
let mut running = true; let mut running = true;
while running { while running {
@ -274,47 +280,52 @@ impl GameBoyAdvance {
self.io_devs.haltcnt = HaltState::Running; self.io_devs.haltcnt = HaltState::Running;
} else { } else {
self.scheduler.fast_forward_to_next(); self.scheduler.fast_forward_to_next();
let (event, cycles_late) = self self.handle_events(&mut running)
.scheduler
.pop_pending_event()
.unwrap_or_else(|| unreachable!());
self.handle_event(event, cycles_late, &mut running);
} }
} }
} }
} }
while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() { self.handle_events(&mut running);
self.handle_event(event, cycles_late, &mut running);
}
} }
let total_cycles_ran = self.scheduler.timestamp() - run_start_time; let total_cycles_ran = self.scheduler.timestamp() - run_start_time;
total_cycles_ran - cycles_to_run total_cycles_ran - cycles_to_run
} }
#[inline] fn handle_events(&mut self, run_limit_flag: &mut bool) {
fn handle_event(&mut self, event: EventType, cycles_late: usize, running: &mut bool) {
let io = &mut (*self.io_devs); let io = &mut (*self.io_devs);
match event { 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 => { EventType::RunLimitReached => {
*running = false; *run_limit_flag = false;
None
}
EventType::DmaActivateChannel(channel_id) => {
io.dmac.activate_channel(channel_id);
None
} }
EventType::DmaActivateChannel(channel_id) => io.dmac.activate_channel(channel_id),
EventType::TimerOverflow(channel_id) => { EventType::TimerOverflow(channel_id) => {
let timers = &mut io.timers; let timers = &mut io.timers;
let dmac = &mut io.dmac; let dmac = &mut io.dmac;
let apu = &mut io.sound; let apu = &mut io.sound;
timers.handle_overflow_event(channel_id, cycles_late, apu, dmac); Some(timers.handle_overflow_event(channel_id, event_time, apu, dmac))
} }
EventType::Gpu(event) => io.gpu.on_event( EventType::Gpu(gpu_event) => Some(io.gpu.on_event(
event, gpu_event,
cycles_late,
&mut *self.sysbus, &mut *self.sysbus,
#[cfg(not(feature = "no_video_interface"))] #[cfg(not(feature = "no_video_interface"))]
&self.video_device, &self.video_device,
), )),
EventType::Apu(event) => io.sound.on_event(event, cycles_late, &self.audio_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)
}
} }
} }

View file

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use super::bus::*; use super::bus::*;
use super::dma::{DmaNotifer, TIMING_HBLANK, TIMING_VBLANK}; use super::dma::{DmaNotifer, TIMING_HBLANK, TIMING_VBLANK};
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::sched::*; use super::sched::{EventType, FutureEvent, GpuEvent, Scheduler};
pub use super::sysbus::consts::*; pub use super::sysbus::consts::*;
#[cfg(not(feature = "no_video_interface"))] #[cfg(not(feature = "no_video_interface"))]
use super::VideoInterface; use super::VideoInterface;
@ -113,11 +113,6 @@ type VideoDeviceRcRefCell = Rc<RefCell<dyn VideoInterface>>;
pub struct Gpu { pub struct Gpu {
interrupt_flags: SharedInterruptFlags, 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 ? /// how many cycles left until next gpu state ?
cycles_left_for_current_state: usize, cycles_left_for_current_state: usize,
@ -153,15 +148,11 @@ impl InterruptConnect for Gpu {
} }
} }
impl SchedulerConnect for Gpu { type FutureGpuEvent = (GpuEvent, usize);
fn connect_scheduler(&mut self, scheduler: SharedScheduler) {
self.scheduler = scheduler;
}
}
impl Gpu { impl Gpu {
pub fn new(mut scheduler: SharedScheduler, interrupt_flags: SharedInterruptFlags) -> Gpu { pub fn new(sched: &mut Scheduler, interrupt_flags: SharedInterruptFlags) -> Gpu {
scheduler.push_gpu_event(GpuEvent::HDraw, CYCLES_HDRAW); sched.schedule((EventType::Gpu(GpuEvent::HDraw), CYCLES_HDRAW));
fn alloc_scanline_buffer() -> Box<[Rgb15]> { fn alloc_scanline_buffer() -> Box<[Rgb15]> {
vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH].into_boxed_slice() vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH].into_boxed_slice()
@ -169,7 +160,6 @@ impl Gpu {
Gpu { Gpu {
interrupt_flags, interrupt_flags,
scheduler,
dispcnt: DisplayControl::from(0x80), dispcnt: DisplayControl::from(0x80),
dispstat: Default::default(), dispstat: Default::default(),
bgcnt: Default::default(), bgcnt: Default::default(),
@ -367,7 +357,7 @@ impl Gpu {
} }
#[inline] #[inline]
fn handle_hdraw_end<D: DmaNotifer>(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) { fn handle_hdraw_end<D: DmaNotifer>(&mut self, dma_notifier: &mut D) -> FutureGpuEvent {
self.dispstat.hblank_flag = true; self.dispstat.hblank_flag = true;
if self.dispstat.hblank_irq_enable { if self.dispstat.hblank_irq_enable {
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank);
@ -382,7 +372,7 @@ impl Gpu {
&mut self, &mut self,
dma_notifier: &mut D, dma_notifier: &mut D,
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
) -> (GpuEvent, usize) { ) -> FutureGpuEvent {
self.update_vcount(self.vcount + 1); self.update_vcount(self.vcount + 1);
if self.vcount < DISPLAY_HEIGHT { 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; self.dispstat.hblank_flag = true;
if self.dispstat.hblank_irq_enable { if self.dispstat.hblank_irq_enable {
interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank);
@ -427,7 +417,7 @@ impl Gpu {
(GpuEvent::VBlankHBlank, CYCLES_HBLANK) (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 { if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 {
self.update_vcount(self.vcount + 1); self.update_vcount(self.vcount + 1);
self.dispstat.hblank_flag = false; self.dispstat.hblank_flag = false;
@ -444,13 +434,13 @@ impl Gpu {
pub fn on_event<D>( pub fn on_event<D>(
&mut self, &mut self,
event: GpuEvent, event: GpuEvent,
extra_cycles: usize,
dma_notifier: &mut D, dma_notifier: &mut D,
#[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell,
) where ) -> FutureEvent
where
D: DmaNotifer, D: DmaNotifer,
{ {
let (next_event, cycles) = match event { let (event, when) = match event {
GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier), GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier),
GpuEvent::HBlank => self.handle_hblank_end( GpuEvent::HBlank => self.handle_hblank_end(
dma_notifier, dma_notifier,
@ -460,8 +450,7 @@ impl Gpu {
GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(), GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(),
GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(), GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(),
}; };
self.scheduler (EventType::Gpu(event), when)
.push(EventType::Gpu(next_event), cycles - extra_cycles);
} }
} }
@ -573,8 +562,8 @@ mod tests {
#[test] #[test]
fn test_gpu_state_machine() { fn test_gpu_state_machine() {
let mut sched = Scheduler::new_shared(); let mut sched = Scheduler::new();
let mut gpu = Gpu::new(sched.clone(), Rc::new(Cell::new(Default::default()))); let mut gpu = Gpu::new(&mut sched, Rc::new(Cell::new(Default::default())));
#[cfg(not(feature = "no_video_interface"))] #[cfg(not(feature = "no_video_interface"))]
let video = Rc::new(RefCell::new(TestVideoInterface::default())); let video = Rc::new(RefCell::new(TestVideoInterface::default()));
#[cfg(not(feature = "no_video_interface"))] #[cfg(not(feature = "no_video_interface"))]

View file

@ -8,7 +8,7 @@ use super::gpu::*;
use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags}; use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags};
use super::keypad; use super::keypad;
use super::mgba_debug::DebugPort; use super::mgba_debug::DebugPort;
use super::sched::{SchedulerConnect, SharedScheduler}; use super::sched::{Scheduler, SchedulerConnect, SharedScheduler};
use super::sound::SoundController; use super::sound::SoundController;
use super::sysbus::SysBusPtr; use super::sysbus::SysBusPtr;
use super::timer::Timers; use super::timer::Timers;
@ -40,6 +40,9 @@ pub struct IoDevices {
// HACK // HACK
// my ownership design sucks // my ownership design sucks
#[serde(skip)] #[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
#[serde(skip)]
#[serde(default = "SysBusPtr::default")] #[serde(default = "SysBusPtr::default")]
sysbus_ptr: SysBusPtr, sysbus_ptr: SysBusPtr,
} }
@ -51,6 +54,7 @@ impl IoDevices {
dmac: DmaController, dmac: DmaController,
timers: Timers, timers: Timers,
sound_controller: Box<SoundController>, sound_controller: Box<SoundController>,
scheduler: SharedScheduler,
) -> IoDevices { ) -> IoDevices {
IoDevices { IoDevices {
intc, intc,
@ -63,7 +67,7 @@ impl IoDevices {
keyinput: keypad::KEYINPUT_ALL_RELEASED, keyinput: keypad::KEYINPUT_ALL_RELEASED,
waitcnt: WaitControl(0), waitcnt: WaitControl(0),
debug: DebugPort::new(), debug: DebugPort::new(),
scheduler: scheduler,
sysbus_ptr: Default::default(), sysbus_ptr: Default::default(),
} }
} }
@ -84,10 +88,7 @@ impl InterruptConnect for IoDevices {
impl SchedulerConnect for IoDevices { impl SchedulerConnect for IoDevices {
fn connect_scheduler(&mut self, scheduler: SharedScheduler) { fn connect_scheduler(&mut self, scheduler: SharedScheduler) {
self.gpu.connect_scheduler(scheduler.clone()); self.scheduler = scheduler.clone();
self.sound.connect_scheduler(scheduler.clone());
self.dmac.connect_scheduler(scheduler.clone());
self.timers.connect_scheduler(scheduler.clone());
} }
} }
@ -124,7 +125,7 @@ impl Bus for IoDevices {
REG_IE => io.intc.interrupt_enable.0 as u16, REG_IE => io.intc.interrupt_enable.0 as u16,
REG_IF => io.intc.interrupt_flags.get().value() 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), SOUND_BASE..=SOUND_END => io.sound.handle_read(io_addr),
REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0, 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_IE => io.intc.interrupt_enable.0 = value,
REG_IF => io.intc.clear(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 => { SOUND_BASE..=SOUND_END => {
io.sound.handle_write(io_addr, value); io.sound.handle_write(io_addr, value);
@ -272,7 +275,8 @@ impl Bus for IoDevices {
DMA_BASE..=REG_DMA3CNT_H => { DMA_BASE..=REG_DMA3CNT_H => {
let ofs = io_addr - DMA_BASE; let ofs = io_addr - DMA_BASE;
let channel_id = (ofs / 12) as usize; 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 => { REG_WAITCNT => {

View file

@ -1,4 +1,3 @@
use std::cell::Cell;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
@ -42,27 +41,21 @@ pub struct Event {
typ: EventType, typ: EventType,
/// Timestamp in cycles /// Timestamp in cycles
time: usize, time: usize,
cancel: Cell<bool>,
} }
impl Event { impl Event {
fn new(typ: EventType, time: usize) -> Event { pub fn new(typ: EventType, time: usize) -> Event {
Event { Event { typ, time }
typ,
time,
cancel: Cell::new(false),
}
} }
#[inline] #[inline]
fn get_type(&self) -> EventType { fn get_type(&self) -> EventType {
self.typ self.typ
} }
}
fn is_canceled(&self) -> bool { /// Future event is an event to be scheduled in x cycles from now
self.cancel.get() pub type FutureEvent = (EventType, usize);
}
}
impl Ord for Event { impl Ord for Event {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
@ -140,27 +133,27 @@ impl Scheduler {
self.events.peek().map(|e| e.typ) self.events.peek().map(|e| e.typ)
} }
/// Schedule an event to be executed in `cycles` cycles from now /// Schedule an event to be executed in `when` cycles from now
pub fn push(&mut self, typ: EventType, cycles: usize) { pub fn schedule(&mut self, event: FutureEvent) {
let event = Event::new(typ, self.timestamp + cycles); let (typ, when) = event;
let event = Event::new(typ, self.timestamp + when);
self.events.push(event); 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` /// Cancel all events with type `typ`
/// This method is rather expansive to call /// This method is rather expansive to call since we are reallocating the entire event tree
pub fn cancel(&mut self, typ: EventType) { pub fn cancel_pending(&mut self, typ: EventType) {
let mut new_events = BinaryHeap::with_capacity(NUM_EVENTS);
self.events self.events
.iter() .iter()
.filter(|e| e.typ == typ) .filter(|e| e.typ != typ)
.for_each(|e| e.cancel.set(true)); .for_each(|e| new_events.push(e.clone()));
} self.events = new_events;
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);
} }
/// Updates the scheduler timestamp /// Updates the scheduler timestamp
@ -174,11 +167,7 @@ impl Scheduler {
if self.timestamp >= event.time { if self.timestamp >= event.time {
// remove the event // remove the event
let event = self.events.pop().unwrap_or_else(|| unreachable!()); let event = self.events.pop().unwrap_or_else(|| unreachable!());
if !event.is_canceled() { Some((event.get_type(), event.time))
Some((event.get_type(), self.timestamp - event.time))
} else {
None
}
} else { } else {
None None
} }

View file

@ -63,10 +63,6 @@ type AudioDeviceRcRefCell = Rc<RefCell<dyn AudioInterface>>;
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SoundController { pub struct SoundController {
#[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
cycles: usize, // cycles count when we last provided a new sample. cycles: usize, // cycles count when we last provided a new sample.
mse: bool, mse: bool,
@ -105,19 +101,12 @@ pub struct SoundController {
output_buffer: Vec<StereoSample<f32>>, output_buffer: Vec<StereoSample<f32>>,
} }
impl SchedulerConnect for SoundController {
fn connect_scheduler(&mut self, scheduler: SharedScheduler) {
self.scheduler = scheduler;
}
}
impl SoundController { 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 resampler = CosineResampler::new(32768_f32, audio_device_sample_rate);
let cycles_per_sample = 512; let cycles_per_sample = 512;
scheduler.push(EventType::Apu(ApuEvent::Sample), cycles_per_sample); sched.schedule((EventType::Apu(ApuEvent::Sample), cycles_per_sample));
SoundController { SoundController {
scheduler,
cycles_per_sample, cycles_per_sample,
cycles: 0, cycles: 0,
mse: false, mse: false,
@ -334,7 +323,7 @@ impl SoundController {
} }
#[inline] #[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]; let mut sample = [0f32; 2];
for channel in 0..=1 { for channel in 0..=1 {
@ -360,20 +349,17 @@ impl SoundController {
(right.round() as i16) * (std::i16::MAX / 512), (right.round() as i16) * (std::i16::MAX / 512),
]); ]);
}); });
(EventType::Apu(ApuEvent::Sample), self.cycles_per_sample)
self.scheduler
.push_apu_event(ApuEvent::Sample, self.cycles_per_sample - extra_cycles);
} }
pub fn on_event( pub fn on_event(
&mut self, &mut self,
event: ApuEvent, event: ApuEvent,
extra_cycles: usize,
audio_device: &AudioDeviceRcRefCell, audio_device: &AudioDeviceRcRefCell,
) { ) -> FutureEvent {
match event { match event {
ApuEvent::Sample => self.on_sample(extra_cycles, audio_device), ApuEvent::Sample => self.on_sample(audio_device),
_ => debug!("got {:?} event", event), _ => unimplemented!("got {:?} event", event),
} }
} }
} }

View file

@ -161,7 +161,8 @@ pub type SysBusPtr = WeakPointer<SysBus>;
impl SchedulerConnect for SysBus { impl SchedulerConnect for SysBus {
fn connect_scheduler(&mut self, scheduler: SharedScheduler) { fn connect_scheduler(&mut self, scheduler: SharedScheduler) {
self.scheduler = scheduler; self.scheduler = scheduler.clone();
self.io.connect_scheduler(scheduler.clone());
} }
} }

View file

@ -1,7 +1,7 @@
use super::dma::DmaController; use super::dma::DmaController;
use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags}; use super::interrupt::{self, Interrupt, InterruptConnect, SharedInterruptFlags};
use super::iodev::consts::*; use super::iodev::consts::*;
use super::sched::{EventType, Scheduler, SchedulerConnect, SharedScheduler}; use super::sched::{EventType, FutureEvent, Scheduler};
use super::sound::SoundController; use super::sound::SoundController;
use num::FromPrimitive; use num::FromPrimitive;
@ -93,9 +93,6 @@ impl Timer {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Timers { pub struct Timers {
#[serde(skip)]
#[serde(default = "Scheduler::new_shared")]
scheduler: SharedScheduler,
timers: [Timer; 4], timers: [Timer; 4],
running_timers: u8, 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<usize> for Timers { impl std::ops::Index<usize> for Timers {
type Output = Timer; type Output = Timer;
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
@ -131,9 +122,8 @@ impl std::ops::IndexMut<usize> for Timers {
} }
impl Timers { impl Timers {
pub fn new(interrupt_flags: SharedInterruptFlags, scheduler: SharedScheduler) -> Timers { pub fn new(interrupt_flags: SharedInterruptFlags) -> Timers {
Timers { Timers {
scheduler,
timers: [ timers: [
Timer::new(0, interrupt_flags.clone()), Timer::new(0, interrupt_flags.clone()),
Timer::new(1, 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]; let timer = &mut self.timers[id];
timer.is_scheduled = true; 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; 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) { fn cancel_timer_event(&mut self, id: usize, sched: &mut Scheduler) {
self.scheduler.cancel(EventType::TimerOverflow(id)); sched.cancel_pending(EventType::TimerOverflow(id));
self[id].is_scheduled = false; self[id].is_scheduled = false;
} }
@ -185,22 +175,15 @@ impl Timers {
pub fn handle_overflow_event( pub fn handle_overflow_event(
&mut self, &mut self,
id: usize, id: usize,
extra_cycles: usize, overflow_time: usize,
apu: &mut SoundController, apu: &mut SoundController,
dmac: &mut DmaController, dmac: &mut DmaController,
) { ) -> FutureEvent {
self.handle_timer_overflow(id, apu, dmac); self.handle_timer_overflow(id, apu, dmac);
self.prepare_next_overflow_event(id, overflow_time)
// 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) { pub fn write_timer_ctl(&mut self, id: usize, value: u16, sched: &mut Scheduler) {
let timer = &mut self.timers[id]; let timer = &mut self.timers[id];
let new_ctl = TimerCtl(value); let new_ctl = TimerCtl(value);
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
@ -211,11 +194,11 @@ impl Timers {
timer.ctl = new_ctl; timer.ctl = new_ctl;
if new_enabled && !cascade { if new_enabled && !cascade {
self.running_timers |= 1 << id; self.running_timers |= 1 << id;
self.cancel_timer_event(id); self.cancel_timer_event(id, sched);
self.add_timer_event(id); sched.schedule(self.prepare_next_overflow_event(id, sched.timestamp()));
} else { } else {
self.running_timers &= !(1 << id); self.running_timers &= !(1 << id);
self.cancel_timer_event(id); self.cancel_timer_event(id, sched);
} }
#[cfg(feature = "debugger")] #[cfg(feature = "debugger")]
@ -231,56 +214,56 @@ impl Timers {
} }
#[inline] #[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]; let timer = &mut self.timers[id];
if timer.is_scheduled { 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 // the current value of the counter
timer.sync_timer_data(self.scheduler.timestamp()); timer.sync_timer_data(sched.timestamp());
} }
timer.data 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 { match io_addr {
REG_TM0CNT_H => self.timers[0].ctl.0, REG_TM0CNT_H => self.timers[0].ctl.0,
REG_TM1CNT_H => self.timers[1].ctl.0, REG_TM1CNT_H => self.timers[1].ctl.0,
REG_TM2CNT_H => self.timers[2].ctl.0, REG_TM2CNT_H => self.timers[2].ctl.0,
REG_TM3CNT_H => self.timers[3].ctl.0, REG_TM3CNT_H => self.timers[3].ctl.0,
REG_TM0CNT_L => self.read_timer_data(0), REG_TM0CNT_L => self.read_timer_data(0, sched),
REG_TM1CNT_L => self.read_timer_data(1), REG_TM1CNT_L => self.read_timer_data(1, sched),
REG_TM2CNT_L => self.read_timer_data(2), REG_TM2CNT_L => self.read_timer_data(2, sched),
REG_TM3CNT_L => self.read_timer_data(3), REG_TM3CNT_L => self.read_timer_data(3, sched),
_ => unreachable!(), _ => 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 { match io_addr {
REG_TM0CNT_L => { REG_TM0CNT_L => {
self.timers[0].data = value; self.timers[0].data = value;
self.timers[0].initial_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 => { REG_TM1CNT_L => {
self.timers[1].data = value; self.timers[1].data = value;
self.timers[1].initial_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 => { REG_TM2CNT_L => {
self.timers[2].data = value; self.timers[2].data = value;
self.timers[2].initial_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 => { REG_TM3CNT_L => {
self.timers[3].data = value; self.timers[3].data = value;
self.timers[3].initial_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!(), _ => unreachable!(),
} }
} }