libsidplayfp  1.0.3
timer.h
1 /*
2  * This file is part of libsidplayfp, a SID player engine.
3  *
4  * Copyright 2011-2013 Leandro Nini <drfiemost@users.sourceforge.net>
5  * Copyright 2007-2010 Antti Lankila
6  * Copyright 2000 Simon White
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #ifndef TIMER_H
24 #define TIMER_H
25 
26 #include <stdint.h>
27 
28 #include "sidplayfp/EventScheduler.h"
29 
30 class MOS6526;
31 
37 class Timer : private Event
38 {
39 protected:
40  static const int_least32_t CIAT_CR_START = 0x01;
41  static const int_least32_t CIAT_STEP = 0x04;
42  static const int_least32_t CIAT_CR_ONESHOT = 0x08;
43  static const int_least32_t CIAT_CR_FLOAD = 0x10;
44  static const int_least32_t CIAT_PHI2IN = 0x20;
45  static const int_least32_t CIAT_CR_MASK = CIAT_CR_START | CIAT_CR_ONESHOT | CIAT_CR_FLOAD | CIAT_PHI2IN;
46 
47  static const int_least32_t CIAT_COUNT2 = 0x100;
48  static const int_least32_t CIAT_COUNT3 = 0x200;
49 
50  static const int_least32_t CIAT_ONESHOT0 = 0x08 << 8;
51  static const int_least32_t CIAT_ONESHOT = 0x08 << 16;
52  static const int_least32_t CIAT_LOAD1 = 0x10 << 8;
53  static const int_least32_t CIAT_LOAD = 0x10 << 16;
54 
55  static const int_least32_t CIAT_OUT = 0x80000000;
56 
57 private:
58  EventCallback<Timer> m_cycleSkippingEvent;
59 
63  EventContext &event_context;
64 
73  event_clock_t ciaEventPauseTime;
74 
78  uint_least16_t timer;
79 
83  uint_least16_t latch;
84 
88  bool pbToggle;
89 
93  uint8_t lastControlValue;
94 
95 protected:
99  MOS6526* const parent;
100 
104  int_least32_t state;
105 
106 private:
110  void cycleSkippingEvent();
111 
115  void clock();
116 
122  inline void reschedule();
123 
127  void event();
128 
132  virtual void underFlow() =0;
133 
137  virtual void serialPort() {};
138 
139 protected:
147  Timer(const char* name, EventContext *context, MOS6526* parent) :
148  Event(name),
149  m_cycleSkippingEvent("Skip CIA clock decrement cycles", *this, &Timer::cycleSkippingEvent),
150  event_context(*context),
151  timer(0),
152  latch(0),
153  pbToggle(false),
154  lastControlValue(0),
155  parent(parent),
156  state(0) {}
157 
158 public:
165  void setControlRegister(uint8_t cr);
166 
172  void syncWithCpu();
173 
179  void wakeUpAfterSyncWithCpu();
180 
184  void reset();
185 
192  void latchLo(uint8_t data);
193 
200  void latchHi(uint8_t data);
201 
208  inline void setPbToggle(bool state) { pbToggle = state; }
209 
215  inline int_least32_t getState() const { return state; }
216 
222  inline uint_least16_t getTimer() const { return timer; }
223 
230  inline bool getPb(uint8_t reg) const { return (reg & 0x04) ? pbToggle : (state & CIAT_OUT); }
231 };
232 
233 void Timer::reschedule()
234 {
235  /* There are only two subcases to consider.
236  *
237  * - are we counting, and if so, are we going to
238  * continue counting?
239  * - have we stopped, and are there no conditions to force a new beginning?
240  *
241  * Additionally, there are numerous flags that are present only in passing manner,
242  * but which we need to let cycle through the CIA state machine.
243  */
244  const int_least32_t unwanted = CIAT_OUT | CIAT_CR_FLOAD | CIAT_LOAD1 | CIAT_LOAD;
245  if ((state & unwanted) != 0)
246  {
247  event_context.schedule(*this, 1);
248  return;
249  }
250 
251  if ((state & CIAT_COUNT3) != 0)
252  {
253  /* Test the conditions that keep COUNT2 and thus COUNT3 alive, and also
254  * ensure that all of them are set indicating steady state operation. */
255 
256  const int_least32_t wanted = CIAT_CR_START | CIAT_PHI2IN | CIAT_COUNT2 | CIAT_COUNT3;
257  if (timer > 2 && (state & wanted) == wanted)
258  {
259  /* we executed this cycle, therefore the pauseTime is +1. If we are called
260  * to execute on the very next clock, we need to get 0 because there's
261  * another timer-- in it. */
262  ciaEventPauseTime = event_context.getTime(EVENT_CLOCK_PHI1) + 1;
263  /* execute event slightly before the next underflow. */
264  event_context.schedule(m_cycleSkippingEvent, timer - 1);
265  return;
266  }
267 
268  /* play safe, keep on ticking. */
269  event_context.schedule(*this, 1);
270  }
271  else
272  {
273  /* Test conditions that result in CIA activity in next clocks.
274  * If none, stop. */
275  const int_least32_t unwanted1 = CIAT_CR_START | CIAT_PHI2IN;
276  const int_least32_t unwanted2 = CIAT_CR_START | CIAT_STEP;
277 
278  if ((state & unwanted1) == unwanted1
279  || (state & unwanted2) == unwanted2)
280  {
281  event_context.schedule(*this, 1);
282  return;
283  }
284 
285  ciaEventPauseTime = -1;
286  }
287 }
288 
289 #endif // TIMER_H