rustation/src/timekeeper.rs
Lionel Flandrin 2d26e5fb60 Added savestate support by implementing Encodable/Decodable
Most of the codebase implements Encodable/Decodable now, with the
exception of the BIOS, CD image and gamepads. The BIOS checksum is
stored however, in order for the frontend to know which file to load.
2016-06-07 23:12:55 +02:00

239 lines
6.6 KiB
Rust

//! XXX: All of this is very much *not* overflow tolerant. I'm just
//! hoping that a u64 will work for the time being but with the fixed
//! point representations shifting things around it's probably going
//! to be a problem sooner or later.
use std::{fmt};
/// List of all peripherals requiring a TimeSheet. The value of the
/// enum is used as the index in the timesheet table
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub enum Peripheral {
/// Graphics Processing Unit
Gpu,
/// Timers
Timer0,
Timer1,
Timer2,
/// Gamepad/Memory Card controller
PadMemCard,
/// CD-ROM controller
CdRom,
}
/// Struct keeping track of the various peripheral's emulation advancement.
#[derive(RustcDecodable, RustcEncodable)]
pub struct TimeKeeper {
/// Counter keeping track of the current date. Unit is a period of
/// the CPU clock at 33.8685MHz (~29.5ns)
now: Cycles,
/// Next time a peripheral needs an update
next_sync: Cycles,
/// Time sheets for keeping track of the various peripherals
timesheets: [TimeSheet; 6],
}
impl TimeKeeper {
pub fn new() -> TimeKeeper {
TimeKeeper {
now: 0,
// Force a sync at the start to initialize evrything
next_sync: 0,
timesheets: [TimeSheet::new(); 6],
}
}
pub fn now(&self) -> Cycles {
self.now
}
pub fn tick(&mut self, cycles: Cycles) {
self.now += cycles;
}
/// Synchronize the timesheet for the given peripheral and return
/// the elapsed time synce the last sync.
pub fn sync(&mut self, who: Peripheral) -> Cycles {
self.timesheets[who as usize].sync(self.now)
}
pub fn set_next_sync_delta(&mut self, who: Peripheral, delta: Cycles) {
let date = self.now + delta;
self.timesheets[who as usize].set_next_sync(date);
if date < self.next_sync {
self.next_sync = date;
}
}
/// Set next sync *only* if it's sooner than what's already
/// configured.
pub fn maybe_set_next_sync(&mut self,
who: Peripheral,
date: Cycles) {
let timesheet = &mut self.timesheets[who as usize];
let next_sync = timesheet.next_sync();
if next_sync > date {
timesheet.set_next_sync(date);
}
}
/// Set next sync delta *only* if it's sooner than what's already
/// configured.
pub fn maybe_set_next_sync_delta(&mut self,
who: Peripheral,
delta: Cycles) {
let date = self.now + delta;
self.maybe_set_next_sync(who, date);
}
/// Called by a peripheral when there's no asynchronous event
/// scheduled.
pub fn no_sync_needed(&mut self, who: Peripheral) {
// Instead of disabling the sync completely we can just use a
// distant date. Peripheral's syncs should be idempotent
// anyway.
self.timesheets[who as usize].set_next_sync(Cycles::max_value());
}
pub fn sync_pending(&self) -> bool{
self.next_sync <= self.now
}
pub fn needs_sync(&self, who: Peripheral) -> bool {
self.timesheets[who as usize].needs_sync(self.now)
}
pub fn update_sync_pending(&mut self) {
self.next_sync =
self.timesheets.iter().map(|t| t.next_sync).min().unwrap();
}
}
impl fmt::Display for TimeKeeper {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let now = self.now;
let cpu_freq = ::cpu::CPU_FREQ_HZ as Cycles;
let seconds = now / cpu_freq;
let rem = now % cpu_freq;
write!(fmt, "{}s+{:08}", seconds, rem)
}
}
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
/// Struct used to keep track of individual peripherals
struct TimeSheet {
/// Date of the last synchronization
last_sync: Cycles,
/// Date of the next "forced" sync
next_sync: Cycles,
}
impl TimeSheet {
fn new() -> TimeSheet {
TimeSheet {
last_sync: 0,
// We force a synchronization at startup to initialize
// everything
next_sync: 0,
}
}
/// Forward the time sheet to the current date and return the
/// elapsed time since the last sync.
fn sync(&mut self, now: Cycles) -> Cycles {
let delta = now - self.last_sync;
self.last_sync = now;
delta
}
fn next_sync(&self) -> Cycles {
self.next_sync
}
fn set_next_sync(&mut self, when: Cycles) {
self.next_sync = when;
}
fn needs_sync(&self, now: Cycles) -> bool {
self.next_sync <= now
}
}
/// 64bit timestamps will wrap in roughly 17271 years with a CPU clock
/// at 33.8685MHz so it should be plenty enough.
pub type Cycles = u64;
/// Fixed point representation of a cycle counter used to store
/// non-integer cycle counts. Required because the CPU and GPU clocks
/// have a non-integer ratio.
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub struct FracCycles(Cycles);
impl FracCycles {
pub fn from_fp(val: Cycles) -> FracCycles {
FracCycles(val)
}
pub fn from_f32(val: f32) -> FracCycles {
let precision = (1u32 << FracCycles::frac_bits()) as f32;
FracCycles((val * precision) as Cycles)
}
pub fn from_cycles(val: Cycles) -> FracCycles {
FracCycles(val << FracCycles::frac_bits())
}
/// Return the raw fixed point value
pub fn get_fp(self) -> Cycles {
self.0
}
/// Return the number of fractional bits in the fixed point
/// representation
pub fn frac_bits() -> Cycles {
16
}
pub fn add(self, val: FracCycles) -> FracCycles {
FracCycles(self.get_fp() + val.get_fp())
}
pub fn multiply(self, mul: FracCycles) -> FracCycles {
let v = self.get_fp() * mul.get_fp();
// The shift amount is doubled during the multiplication so we
// have to shift it back to its normal position.
FracCycles(v >> FracCycles::frac_bits())
}
pub fn divide(self, denominator: FracCycles) -> FracCycles {
// In order not to lose precision we must shift the numerator
// once more *before* the division. Otherwise the division of
// the two shifted value would only give us the integer part
// of the result.
let numerator = self.get_fp() << FracCycles::frac_bits();
FracCycles(numerator / denominator.get_fp())
}
pub fn ceil(self) -> Cycles {
let shift = FracCycles::frac_bits();
let align = (1 << shift) - 1;
(self.0 + align) >> shift
}
}