libsidplayfp 1.0.3
timer.h
00001 /*
00002  * This file is part of libsidplayfp, a SID player engine.
00003  *
00004  * Copyright 2011-2013 Leandro Nini <drfiemost@users.sourceforge.net>
00005  * Copyright 2007-2010 Antti Lankila
00006  * Copyright 2000 Simon White
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00021  */
00022 
00023 #ifndef TIMER_H
00024 #define TIMER_H
00025 
00026 #include <stdint.h>
00027 
00028 #include "sidplayfp/EventScheduler.h"
00029 
00030 class MOS6526;
00031 
00037 class Timer : private Event
00038 {
00039 protected:
00040     static const int_least32_t CIAT_CR_START   = 0x01;
00041     static const int_least32_t CIAT_STEP       = 0x04;
00042     static const int_least32_t CIAT_CR_ONESHOT = 0x08;
00043     static const int_least32_t CIAT_CR_FLOAD   = 0x10;
00044     static const int_least32_t CIAT_PHI2IN     = 0x20;
00045     static const int_least32_t CIAT_CR_MASK    = CIAT_CR_START | CIAT_CR_ONESHOT | CIAT_CR_FLOAD | CIAT_PHI2IN;
00046 
00047     static const int_least32_t CIAT_COUNT2     = 0x100;
00048     static const int_least32_t CIAT_COUNT3     = 0x200;
00049 
00050     static const int_least32_t CIAT_ONESHOT0   = 0x08 << 8;
00051     static const int_least32_t CIAT_ONESHOT    = 0x08 << 16;
00052     static const int_least32_t CIAT_LOAD1      = 0x10 << 8;
00053     static const int_least32_t CIAT_LOAD       = 0x10 << 16;
00054 
00055     static const int_least32_t CIAT_OUT        = 0x80000000;
00056 
00057 private:
00058     EventCallback<Timer> m_cycleSkippingEvent;
00059 
00063     EventContext &event_context;
00064 
00073     event_clock_t ciaEventPauseTime;
00074 
00078     uint_least16_t timer;
00079 
00083     uint_least16_t latch;
00084 
00088     bool pbToggle;
00089 
00093     uint8_t lastControlValue;
00094 
00095 protected:
00099     MOS6526* const parent;
00100 
00104     int_least32_t state;
00105 
00106 private:
00110     void cycleSkippingEvent();
00111 
00115     void clock();
00116 
00122     inline void reschedule();
00123 
00127     void event();
00128 
00132     virtual void underFlow() =0;
00133 
00137     virtual void serialPort() {};
00138 
00139 protected:
00147     Timer(const char* name, EventContext *context, MOS6526* parent) :
00148         Event(name),
00149         m_cycleSkippingEvent("Skip CIA clock decrement cycles", *this, &Timer::cycleSkippingEvent),
00150         event_context(*context),
00151         timer(0),
00152         latch(0),
00153         pbToggle(false),
00154         lastControlValue(0),
00155         parent(parent),
00156         state(0) {}
00157 
00158 public:
00165     void setControlRegister(uint8_t cr);
00166 
00172     void syncWithCpu();
00173 
00179     void wakeUpAfterSyncWithCpu();
00180 
00184     void reset();
00185 
00192     void latchLo(uint8_t data);
00193 
00200     void latchHi(uint8_t data);
00201 
00208     inline void setPbToggle(bool state) { pbToggle = state; }
00209 
00215     inline int_least32_t getState() const { return state; }
00216 
00222     inline uint_least16_t getTimer() const { return timer; }
00223 
00230     inline bool getPb(uint8_t reg) const { return (reg & 0x04) ? pbToggle : (state & CIAT_OUT); }
00231 };
00232 
00233 void Timer::reschedule()
00234 {
00235     /* There are only two subcases to consider.
00236      *
00237      * - are we counting, and if so, are we going to
00238      *   continue counting?
00239      * - have we stopped, and are there no conditions to force a new beginning?
00240      *
00241      * Additionally, there are numerous flags that are present only in passing manner,
00242      * but which we need to let cycle through the CIA state machine.
00243      */
00244     const int_least32_t unwanted = CIAT_OUT | CIAT_CR_FLOAD | CIAT_LOAD1 | CIAT_LOAD;
00245     if ((state & unwanted) != 0)
00246     {
00247         event_context.schedule(*this, 1);
00248         return;
00249     }
00250 
00251     if ((state & CIAT_COUNT3) != 0)
00252     {
00253         /* Test the conditions that keep COUNT2 and thus COUNT3 alive, and also
00254          * ensure that all of them are set indicating steady state operation. */
00255 
00256         const int_least32_t wanted = CIAT_CR_START | CIAT_PHI2IN | CIAT_COUNT2 | CIAT_COUNT3;
00257         if (timer > 2 && (state & wanted) == wanted)
00258         {
00259             /* we executed this cycle, therefore the pauseTime is +1. If we are called
00260              * to execute on the very next clock, we need to get 0 because there's
00261              * another timer-- in it. */
00262             ciaEventPauseTime = event_context.getTime(EVENT_CLOCK_PHI1) + 1;
00263             /* execute event slightly before the next underflow. */
00264             event_context.schedule(m_cycleSkippingEvent, timer - 1);
00265             return;
00266         }
00267 
00268         /* play safe, keep on ticking. */
00269         event_context.schedule(*this, 1);
00270     }
00271     else
00272     {
00273         /* Test conditions that result in CIA activity in next clocks.
00274          * If none, stop. */
00275         const int_least32_t unwanted1 = CIAT_CR_START | CIAT_PHI2IN;
00276         const int_least32_t unwanted2 = CIAT_CR_START | CIAT_STEP;
00277 
00278         if ((state & unwanted1) == unwanted1
00279             || (state & unwanted2) == unwanted2)
00280         {
00281             event_context.schedule(*this, 1);
00282             return;
00283         }
00284 
00285         ciaEventPauseTime = -1;
00286     }
00287 }
00288 
00289 #endif // TIMER_H