timer.h
/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2013 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 2000 Simon White
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TIMER_H
#define TIMER_H
#include <stdint.h>
#include "Event.h"
#include "EventCallback.h"
#include "EventScheduler.h"
#include "sidcxx11.h"
namespace libsidplayfp
{
class MOS652X;
class Timer : private Event
{
protected:
static constexpr int_least32_t CIAT_CR_START = 0x01;
static constexpr int_least32_t CIAT_STEP = 0x04;
static constexpr int_least32_t CIAT_CR_ONESHOT = 0x08;
static constexpr int_least32_t CIAT_CR_FLOAD = 0x10;
static constexpr int_least32_t CIAT_PHI2IN = 0x20;
static constexpr int_least32_t CIAT_CR_MASK = CIAT_CR_START | CIAT_CR_ONESHOT | CIAT_CR_FLOAD | CIAT_PHI2IN;
static constexpr int_least32_t CIAT_COUNT2 = 0x100;
static constexpr int_least32_t CIAT_COUNT3 = 0x200;
static constexpr int_least32_t CIAT_ONESHOT0 = 0x08 << 8;
static constexpr int_least32_t CIAT_ONESHOT = 0x08 << 16;
static constexpr int_least32_t CIAT_LOAD1 = 0x10 << 8;
static constexpr int_least32_t CIAT_LOAD = 0x10 << 16;
static constexpr int_least32_t CIAT_OUT = 0x80000000;
private:
EventCallback<Timer> m_cycleSkippingEvent;
EventScheduler &eventScheduler;
event_clock_t ciaEventPauseTime;
bool pbToggle = false;
uint_least16_t timer = 0;
uint_least16_t latch = 0;
uint8_t lastControlValue = 0;
protected:
MOS652X &parent;
int_least32_t state = 0;
private:
void cycleSkippingEvent();
void clock();
inline void reschedule();
void event() override;
virtual void underFlow() =0;
virtual void serialPort() {}
protected:
Timer(const char* name, EventScheduler &scheduler, MOS652X &parent) :
Event(name),
m_cycleSkippingEvent("Skip CIA clock decrement cycles", *this, &Timer::cycleSkippingEvent),
eventScheduler(scheduler),
parent(parent) {}
public:
void setControlRegister(uint8_t cr);
void syncWithCpu();
void wakeUpAfterSyncWithCpu();
void reset();
void latchLo(uint8_t data);
void latchHi(uint8_t data);
inline void setPbToggle(bool state) { pbToggle = state; }
inline int_least32_t getState() const { return state; }
inline uint_least16_t getTimer() const { return timer; }
inline bool getPb(uint8_t reg) const { return (reg & 0x04) ? pbToggle : (state & CIAT_OUT); }
};
void Timer::reschedule()
{
// There are only two subcases to consider.
//
// - are we counting, and if so, are we going to
// continue counting?
// - have we stopped, and are there no conditions to force a new beginning?
//
// Additionally, there are numerous flags that are present only in passing manner,
// but which we need to let cycle through the CIA state machine.
const int_least32_t unwanted = CIAT_OUT | CIAT_CR_FLOAD | CIAT_LOAD1 | CIAT_LOAD;
if ((state & unwanted) != 0)
{
eventScheduler.schedule(*this, 1);
return;
}
if ((state & CIAT_COUNT3) != 0)
{
// Test the conditions that keep COUNT2 and thus COUNT3 alive, and also
// ensure that all of them are set indicating steady state operation.
const int_least32_t wanted = CIAT_CR_START | CIAT_PHI2IN | CIAT_COUNT2 | CIAT_COUNT3;
if (timer > 2 && (state & wanted) == wanted)
{
// we executed this cycle, therefore the pauseTime is +1. If we are called
// to execute on the very next clock, we need to get 0 because there's
// another timer-- in it.
ciaEventPauseTime = eventScheduler.getTime(EVENT_CLOCK_PHI1) + 1;
// execute event slightly before the next underflow.
eventScheduler.schedule(m_cycleSkippingEvent, timer - 1);
return;
}
// play safe, keep on ticking.
eventScheduler.schedule(*this, 1);
}
else
{
// Test conditions that result in CIA activity in next clocks.
// If none, stop.
const int_least32_t unwanted1 = CIAT_CR_START | CIAT_PHI2IN;
const int_least32_t unwanted2 = CIAT_CR_START | CIAT_STEP;
if ((state & unwanted1) == unwanted1
|| (state & unwanted2) == unwanted2)
{
eventScheduler.schedule(*this, 1);
return;
}
ciaEventPauseTime = -1;
}
}
}
#endif // TIMER_H
libsidplayfp::EventCallback
Definition EventCallback.h:36
libsidplayfp::Event::Event
Event(const char *const name)
Definition Event.h:58
libsidplayfp::Event::name
const char * name() const
Definition Event.h:72
libsidplayfp::Event::event
virtual void event()=0
libsidplayfp::MOS652X
Definition mos652x.h:154
libsidplayfp::Timer::reset
void reset()
Definition timer.cpp:131
libsidplayfp::Timer::getTimer
uint_least16_t getTimer() const
Definition timer.h:207
libsidplayfp::Timer::getPb
bool getPb(uint8_t reg) const
Definition timer.h:215
libsidplayfp::Timer::wakeUpAfterSyncWithCpu
void wakeUpAfterSyncWithCpu()
Definition timer.cpp:60
libsidplayfp::Timer::getState
int_least32_t getState() const
Definition timer.h:200
libsidplayfp::Timer::syncWithCpu
void syncWithCpu()
Definition timer.cpp:37
libsidplayfp::Timer::Timer
Timer(const char *name, EventScheduler &scheduler, MOS652X &parent)
Definition timer.h:138
libsidplayfp::Timer::state
int_least32_t state
CRA/CRB control register / state.
Definition timer.h:95
libsidplayfp::Timer::latchHi
void latchHi(uint8_t data)
Definition timer.cpp:149
libsidplayfp::Timer::parent
MOS652X & parent
Pointer to the MOS6526 which this Timer belongs to.
Definition timer.h:92
libsidplayfp::Timer::latchLo
void latchLo(uint8_t data)
Definition timer.cpp:142
libsidplayfp::Timer::setPbToggle
void setPbToggle(bool state)
Definition timer.h:193
libsidplayfp::Timer::setControlRegister
void setControlRegister(uint8_t cr)
Definition timer.cpp:30