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 "sidplayfp/event.h"
#include "sidplayfp/EventScheduler.h"
class MOS6526;
class Timer : private Event
{
protected:
static const int_least32_t CIAT_CR_START = 0x01;
static const int_least32_t CIAT_STEP = 0x04;
static const int_least32_t CIAT_CR_ONESHOT = 0x08;
static const int_least32_t CIAT_CR_FLOAD = 0x10;
static const int_least32_t CIAT_PHI2IN = 0x20;
static const int_least32_t CIAT_CR_MASK = CIAT_CR_START | CIAT_CR_ONESHOT | CIAT_CR_FLOAD | CIAT_PHI2IN;
static const int_least32_t CIAT_COUNT2 = 0x100;
static const int_least32_t CIAT_COUNT3 = 0x200;
static const int_least32_t CIAT_ONESHOT0 = 0x08 << 8;
static const int_least32_t CIAT_ONESHOT = 0x08 << 16;
static const int_least32_t CIAT_LOAD1 = 0x10 << 8;
static const int_least32_t CIAT_LOAD = 0x10 << 16;
static const int_least32_t CIAT_OUT = 0x80000000;
private:
EventCallback<Timer> m_cycleSkippingEvent;
EventContext &event_context;
event_clock_t ciaEventPauseTime;
uint_least16_t timer;
uint_least16_t latch;
bool pbToggle;
uint8_t lastControlValue;
protected:
MOS6526* const parent;
int_least32_t state;
private:
void cycleSkippingEvent();
void clock();
inline void reschedule();
void event();
virtual void underFlow() =0;
virtual void serialPort() {};
protected:
Timer(const char* name, EventContext *context, MOS6526* parent) :
Event(name),
m_cycleSkippingEvent("Skip CIA clock decrement cycles", *this, &Timer::cycleSkippingEvent),
event_context(*context),
timer(0),
latch(0),
pbToggle(false),
lastControlValue(0),
parent(parent),
state(0) {}
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)
{
event_context.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 = event_context.getTime(EVENT_CLOCK_PHI1) + 1;
/* execute event slightly before the next underflow. */
event_context.schedule(m_cycleSkippingEvent, timer - 1);
return;
}
/* play safe, keep on ticking. */
event_context.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)
{
event_context.schedule(*this, 1);
return;
}
ciaEventPauseTime = -1;
}
}
#endif // TIMER_H
MOS6526
Definition: mos6526.h:102
Timer::reset
void reset()
Definition: timer.cpp:128
Timer::latchLo
void latchLo(uint8_t data)
Definition: timer.cpp:139
Timer::setPbToggle
void setPbToggle(bool state)
Definition: timer.h:209
Timer::getPb
bool getPb(uint8_t reg) const
Definition: timer.h:231
EventContext
Definition: event.h:101
Timer::Timer
Timer(const char *name, EventContext *context, MOS6526 *parent)
Definition: timer.h:148
Timer::getState
int_least32_t getState() const
Definition: timer.h:216
EventCallback< Timer >
Timer::parent
MOS6526 *const parent
Definition: timer.h:100
EventContext::schedule
virtual void schedule(Event &event, event_clock_t cycles, event_phase_t phase)=0
Timer::state
int_least32_t state
Definition: timer.h:105
Timer::getTimer
uint_least16_t getTimer() const
Definition: timer.h:223
Timer::latchHi
void latchHi(uint8_t data)
Definition: timer.cpp:146
Event
Definition: event.h:50
Timer::syncWithCpu
void syncWithCpu()
Definition: timer.cpp:34
EventContext::getTime
virtual event_clock_t getTime(event_phase_t phase) const =0
Timer::wakeUpAfterSyncWithCpu
void wakeUpAfterSyncWithCpu()
Definition: timer.cpp:57
Timer
Definition: timer.h:38
Timer::setControlRegister
void setControlRegister(uint8_t cr)
Definition: timer.cpp:27