libsidplayfp  1.5.3
mos656x.h
1 /*
2  * This file is part of libsidplayfp, a SID player engine.
3  *
4  * Copyright 2011-2014 Leandro Nini <drfiemost@users.sourceforge.net>
5  * Copyright 2009-2014 VICE Project
6  * Copyright 2007-2010 Antti Lankila
7  * Copyright 2001 Simon White
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #ifndef MOS656X_H
25 #define MOS656X_H
26 
27 #include <stdint.h>
28 
29 #include "sidplayfp/event.h"
30 #include "sidplayfp/component.h"
31 #include "sidplayfp/EventScheduler.h"
32 
33 
34 class MOS656X: public component, private Event
35 {
36 public:
37  typedef enum
38  {
43  } model_t;
44 
45 private:
46  typedef struct
47  {
48  unsigned int rasterLines;
49  unsigned int cyclesPerLine;
50  event_clock_t (MOS656X::*clock)();
51  } model_data_t;
52 
53 private:
54  static const char *credit;
55 
56  static const model_data_t modelData[];
57 
59  static const int IRQ_RASTER = 1 << 0;
60 
62  static const int IRQ_LIGHTPEN = 1 << 3;
63 
65  static const unsigned int FIRST_DMA_LINE = 0x30;
66 
68  static const unsigned int LAST_DMA_LINE = 0xf7;
69 
70 private:
71  event_clock_t (MOS656X::*clock)();
72 
73  event_clock_t rasterClk;
74 
76  EventContext &event_context;
77 
79  unsigned int cyclesPerLine;
80 
82  unsigned int maxRasters;
83 
85  unsigned int lineCycle;
86 
88  unsigned int rasterY;
89 
91  unsigned int yscroll;
92 
94  bool areBadLinesEnabled;
95 
97  bool isBadLine;
98 
100  bool rasterYIRQCondition;
101 
103  bool vblanking;
104 
106  bool lp_triggered;
107 
109  uint8_t irqFlags;
110 
112  uint8_t irqMask;
113 
115  uint8_t lpx, lpy;
116 
118 
119  uint8_t &sprite_enable, &sprite_y_expansion;
120  uint8_t sprite_exp_flop;
121  uint8_t sprite_dma;
122  uint8_t sprite_mc_base[8];
123  uint8_t sprite_mc[8];
125 
127  uint8_t regs[0x40];
128 
129 private:
130  event_clock_t clockPAL();
131  event_clock_t clockNTSC();
132  event_clock_t clockOldNTSC();
133 
137  void handleIrqState();
138 
139  EventCallback<MOS656X> badLineStateChangeEvent;
140 
144  void badLineStateChange() { setBA(!isBadLine); }
145 
146  EventCallback<MOS656X> rasterYIRQEdgeDetectorEvent;
147 
151  void rasterYIRQEdgeDetector()
152  {
153  const bool oldRasterYIRQCondition = rasterYIRQCondition;
154  rasterYIRQCondition = rasterY == readRasterLineIRQ();
155  if (!oldRasterYIRQCondition && rasterYIRQCondition)
156  activateIRQFlag(IRQ_RASTER);
157  }
158 
163  void activateIRQFlag(int flag)
164  {
165  irqFlags |= flag;
166  handleIrqState();
167  }
168 
174  unsigned int readRasterLineIRQ() const
175  {
176  return (regs[0x12] & 0xff) + ((regs[0x11] & 0x80) << 1);
177  }
178 
184  bool readDEN() const { return (regs[0x11] & 0x10) != 0; }
185 
189  inline unsigned int oldRasterY()
190  {
191  const int prevRasterY = rasterY - 1;
192  return prevRasterY >= 0 ? prevRasterY : cyclesPerLine - 1;
193  }
194 
195  inline void sync()
196  {
197  event_context.cancel(*this);
198  event();
199  }
200 
204  inline void checkVblank()
205  {
206  // IRQ occurred (xraster != 0)
207  if (rasterY == (maxRasters - 1))
208  {
209  vblanking = true;
210  }
211 
212  // Check DEN bit on first cycle of the line following the first DMA line
213  if (rasterY == FIRST_DMA_LINE
214  && !areBadLinesEnabled
215  && readDEN())
216  {
217  areBadLinesEnabled = true;
218  }
219 
220  // Disallow bad lines after the last possible one has passed
221  if (rasterY == LAST_DMA_LINE)
222  {
223  areBadLinesEnabled = false;
224  }
225 
226  isBadLine = false;
227 
228  if (!vblanking)
229  {
230  rasterY++;
231  rasterYIRQEdgeDetector();
232  }
233  }
234 
235  inline void vblank()
236  {
237  // Vertical blank (line 0)
238  if (vblanking)
239  {
240  vblanking = lp_triggered = false;
241  rasterY = 0;
242  rasterYIRQEdgeDetector();
243  }
244  }
245 
250  inline void updateMc()
251  {
252  uint8_t mask = 1;
253  for (unsigned int i=0; i<8; i++, mask<<=1)
254  {
255  if (sprite_dma & mask)
256  sprite_mc[i] = (sprite_mc[i] + 3) & 0x3f;
257  }
258  }
259 
260  inline void updateMcBase()
261  {
262  uint8_t mask = 1;
263  for (unsigned int i=0; i<8; i++, mask<<=1)
264  {
265  if (sprite_exp_flop & mask)
266  {
267  sprite_mc_base[i] = sprite_mc[i];
268  if (sprite_mc_base[i] == 0x3f)
269  sprite_dma &= ~mask;
270  }
271  }
272  }
273 
277  inline void checkSpriteExp()
278  {
279  sprite_exp_flop ^= sprite_dma & sprite_y_expansion;
280  }
281 
285  inline void checkSpriteDma()
286  {
287  const uint8_t y = rasterY & 0xff;
288  uint8_t mask = 1;
289  for (unsigned int i=0; i<8; i++, mask<<=1)
290  {
291  if ((sprite_enable & mask) && (y == regs[(i << 1) + 1]) && !(sprite_dma & mask))
292  {
293  sprite_dma |= mask;
294  sprite_mc_base[i] = 0;
295  sprite_exp_flop |= mask;
296  }
297  }
298  }
299 
300  inline void checkSpriteDisplay()
301  {
302  for (unsigned int i=0; i<8; i++)
303  {
304  sprite_mc[i] = sprite_mc_base[i];
305  }
306  }
307 
311  template<int n>
312  inline void startDma()
313  {
314  if (sprite_dma & (0x01 << n))
315  setBA(false);
316  }
317 
321  template<int n>
322  inline void endDma()
323  {
324  if (!(sprite_dma & (0x06 << n)))
325  setBA(true);
326  }
327 
331  inline void startBadline()
332  {
333  if (isBadLine)
334  setBA(false);
335  }
336 
337 protected:
338  MOS656X(EventContext *context);
339  ~MOS656X() {}
340 
341  // Environment Interface
342  virtual void interrupt (bool state) = 0;
343  virtual void setBA (bool state) = 0;
344 
351  uint8_t read(uint_least8_t addr);
352 
361  void write(uint_least8_t addr, uint8_t data);
362 
363 public:
364  void event();
365 
366  void chip(model_t model);
367  void lightpen();
368 
369  // Component Standard Calls
370  void reset();
371 
372  const char *credits() const { return credit; }
373 };
374 
375 // Template specializations
376 
380 template<>
381 inline void MOS656X::startDma<0>()
382 {
383  setBA(!(sprite_dma & 0x01));
384 }
385 
389 template<>
390 inline void MOS656X::endDma<7>()
391 {
392  setBA(true);
393 }
394 
395 #endif // MOS656X_H