openMSX
VDP.hh
Go to the documentation of this file.
1 #ifndef VDP_HH
2 #define VDP_HH
3 
4 #include "MSXDevice.hh"
5 #include "Schedulable.hh"
7 #include "IRQHelper.hh"
8 #include "Clock.hh"
9 #include "DisplayMode.hh"
10 #include "likely.hh"
11 #include "openmsx.hh"
12 #include <memory>
13 
14 namespace openmsx {
15 
16 class PostProcessor;
17 class Renderer;
18 class VDPCmdEngine;
19 class VDPVRAM;
20 class SpriteChecker;
21 class VDPRegDebug;
22 class VDPStatusRegDebug;
23 class VDPPaletteDebug;
24 class VRAMPointerDebug;
25 class FrameCountInfo;
26 class CycleInFrameInfo;
27 class LineInFrameInfo;
28 class CycleInLineInfo;
29 class MsxYPosInfo;
30 class MsxX256PosInfo;
31 class MsxX512PosInfo;
32 class Display;
33 class RawFrame;
34 class AccessSlotCalculator;
35 
61 class VDP : public MSXDevice, public Schedulable,
63 {
64 public:
67  static const int TICKS_PER_SECOND = 3579545 * 6; // 21.5MHz;
69 
72  static const int TICKS_PER_LINE = 1368;
73 
74  explicit VDP(const DeviceConfig& config);
75  virtual ~VDP();
76 
77  virtual void powerUp(EmuTime::param time);
78  virtual void reset(EmuTime::param time);
79  virtual byte readIO(word port, EmuTime::param time);
80  virtual byte peekIO(word port, EmuTime::param time) const;
81  virtual void writeIO(word port, byte value, EmuTime::param time);
82  virtual void executeUntil(EmuTime::param time, int userData);
83 
89 
94  inline bool isMSX1VDP() const {
95  return version == TMS99X8A || version == TMS9929A;
96  }
97 
101  inline bool hasYJK() const {
102  return version == V9958;
103  }
104 
108  inline DisplayMode getDisplayMode() const {
109  return displayMode;
110  }
111 
114  inline VDPVRAM& getVRAM() {
115  return *vram;
116  }
117 
122  inline const RawFrame* isSuperimposing() const {
123  // Note that bit 0 of r#0 has no effect on an V9938 or higher,
124  // but this bit is masked out. Also note that on an MSX1, if
125  // bit 0 of r#0 is enabled and there is no external video
126  // source, then we lose sync.
127  // Also note that because this property is fixed per frame we
128  // cannot (re)calculate it from register values.
129  return superimposing;
130  }
131 
135  return *spriteChecker;
136  }
137 
141  inline bool getTransparency() const {
142  return (controlRegs[8] & 0x20) == 0;
143  }
144 
148  inline int getForegroundColor() const {
149  return controlRegs[7] >> 4;
150  }
151 
160  inline int getBackgroundColor() const {
161  byte reg7 = controlRegs[7];
162  if (displayMode.getByte() == DisplayMode::GRAPHIC7) {
163  return reg7;
164  } else {
165  return reg7 & 0x0F;
166  }
167  }
168 
172  inline int getBlinkForegroundColor() const {
173  return controlRegs[12] >> 4;
174  }
175 
179  inline int getBlinkBackgroundColor() const {
180  return controlRegs[12] & 0x0F;
181  }
182 
186  inline bool getBlinkState() const {
187  return blinkState;
188  }
189 
195  inline word getPalette(int index) const {
196  return palette[index];
197  }
198 
204  inline bool isDisplayEnabled() const {
205  return isDisplayArea && displayEnabled;
206  }
207 
212  inline bool spritesEnabled() const {
213  return displayEnabled && !displayMode.isTextMode() &&
214  ((controlRegs[8] & 0x02) == 0x00);
215  }
216 
220  inline bool spritesEnabledFast() const {
221  assert(!displayMode.isTextMode());
222  return displayEnabled && ((controlRegs[8] & 0x02) == 0x00);
223  }
224 
228  inline byte getVerticalScroll() const {
229  return controlRegs[23];
230  }
231 
237  inline byte getHorizontalScrollLow() const {
238  return controlRegs[27];
239  }
240 
246  inline byte getHorizontalScrollHigh() const {
247  return controlRegs[26];
248  }
249 
255  inline bool isBorderMasked() const {
256  return (controlRegs[25] & 0x02) != 0;
257  }
258 
265  inline bool isMultiPageScrolling() const {
266  return (controlRegs[25] & 0x01) && (controlRegs[2] & 0x20);
267  }
268 
273  inline int getLineZero() const {
274  return displayStart / TICKS_PER_LINE;
275  }
276 
281  inline bool isPalTiming() const {
282  return palTiming;
283  }
284 
292  inline bool isInterlaced() const {
293  return interlaced;
294  }
295 
307  inline bool isEvenOddEnabled() const {
308  return (controlRegs[9] & 4) != 0;
309  }
310 
314  inline bool getEvenOdd() const {
315  return (statusReg2 & 2) != 0;
316  }
317 
328  inline int getEvenOddMask() const {
329  // TODO: Verify which page is displayed on even fields.
330  return (((~controlRegs[9] & 4) << 6) | ((statusReg2 & 2) << 7)) &
331  (!blinkState << 8);
332  }
333 
337  inline int getTicksThisFrame(EmuTime::param time) const {
338  return frameStartTime.getTicksTill_fast(time);
339  }
340 
342  return frameStartTime.getTime();
343  }
344 
347  inline int getSpriteSize() const {
348  return ((controlRegs[1] & 2) << 2) + 8;
349  }
350 
353  inline bool isSpriteMag() const {
354  return controlRegs[1] & 1;
355  }
356 
360  inline bool getCmdBit() const {
361  return (controlRegs[25] & 0x40) != 0;
362  }
363 
366  inline int getTicksPerFrame() const {
367  return palTiming ? TICKS_PER_LINE * 313 : TICKS_PER_LINE * 262;
368  }
369 
379  inline bool isInsideFrame(EmuTime::param time) const {
380  return time >= frameStartTime.getTime() &&
382  }
383 
391  inline int getLeftSprites() const {
392  return 100 + 102 + 56
393  + (horizontalAdjust - 7) * 4
394  + (displayMode.isTextMode() ? 36 : 0);
395  }
396 
402  inline int getLeftBorder() const {
403  return getLeftSprites() + (isBorderMasked() ? 8 * 4 : 0);
404  }
405 
409  inline int getRightBorder() const {
410  return getLeftSprites()
411  + (displayMode.isTextMode() ? 960 : 1024);
412  }
413 
419  inline int getLeftBackground() const {
420  return getLeftSprites() + getHorizontalScrollLow() * 4;
421  }
422 
426  byte getStatusReg0() const { return statusReg0; }
427 
436  void setSpriteStatus(byte value)
437  {
438  statusReg0 = (statusReg0 & 0x80) | (value & 0x7F);
439  }
440 
444  bool getVRMode() const {
445  return (controlRegs[8] & 8) != 0;
446  }
447 
450  void setExternalVideoSource(const RawFrame* externalSource);
451 
454  EmuTime getAccessSlot(EmuTime::param time, unsigned delta) const;
455 
466 
468  bool cpuAccessScheduled() const;
469 
470  template<typename Archive>
471  void serialize(Archive& ar, unsigned version);
472 
473 private:
476  enum VdpVersion {
482  TMS99X8A,
485  TMS9929A,
488  V9938,
491  V9958
492  };
493 
496  enum SyncType {
498  VSYNC,
500  DISPLAY_START,
502  VSCAN,
504  HSCAN,
506  HOR_ADJUST,
508  SET_MODE,
510  SET_BLANK,
512  CPU_VRAM_ACCESS,
513  };
514 
525  static const int LINE_COUNT_RESET_TICKS = 15 * TICKS_PER_LINE;
526 
530  inline int getNumberOfLines() const {
531  return controlRegs[9] & 0x80 ? 212 : 192;
532  }
533 
543  inline bool getHR(int ticksThisFrame) const {
544  // Note: These constants are located inside this function because
545  // GCC 4.0.x won't link if they are in the class scope.
549  static const int HBLANK_LEN_TXT = 404;
553  static const int HBLANK_LEN_GFX = 312;
554  return
555  ( ticksThisFrame + TICKS_PER_LINE - getRightBorder()
556  ) % TICKS_PER_LINE
557  < (displayMode.isTextMode() ? HBLANK_LEN_TXT : HBLANK_LEN_GFX);
558  }
559 
560  // VideoSystemChangeListener interface:
561  virtual void preVideoSystemChange();
562  virtual void postVideoSystemChange();
563 
568  void resetInit();
569 
574  void resetMasks(EmuTime::param time);
575 
579  void frameStart(EmuTime::param time);
580 
588  void scheduleDisplayStart(EmuTime::param time);
589 
595  void scheduleVScan(EmuTime::param time);
596 
602  void scheduleHScan(EmuTime::param time);
603 
606  void vramWrite(byte value, EmuTime::param time);
607 
610  byte vramRead(EmuTime::param time);
611 
613  void scheduleCpuVramAccess(bool isRead, EmuTime::param time);
614  void executeCpuVramAccess(EmuTime::param time);
615 
618  byte peekStatusReg(byte reg, EmuTime::param time) const;
619  byte readStatusReg(byte reg, EmuTime::param time);
620 
623  void changeRegister(byte reg, byte val, EmuTime::param time);
624 
627  void syncAtNextLine(SyncType type, EmuTime::param time);
628 
631  void createRenderer();
632 
636  void updateNameBase(EmuTime::param time);
637 
641  void updateColorBase(EmuTime::param time);
642 
646  void updatePatternBase(EmuTime::param time);
647 
651  void updateSpriteAttributeBase(EmuTime::param time);
652 
656  void updateSpritePatternBase(EmuTime::param time);
657 
661  void updateDisplayMode(DisplayMode newMode, EmuTime::param time);
662 
669  void setPalette(int index, word grb, EmuTime::param time);
670 
671 private:
672  Display& display;
673 
674  friend class VDPRegDebug;
675  friend class VDPStatusRegDebug;
676  friend class VDPPaletteDebug;
677  friend class VRAMPointerDebug;
678  friend class FrameCountInfo;
679  const std::unique_ptr<VDPRegDebug> vdpRegDebug;
680  const std::unique_ptr<VDPStatusRegDebug> vdpStatusRegDebug;
681  const std::unique_ptr<VDPPaletteDebug> vdpPaletteDebug;
682  const std::unique_ptr<VRAMPointerDebug> vramPointerDebug;
683  const std::unique_ptr<FrameCountInfo> frameCountInfo;
684  const std::unique_ptr<CycleInFrameInfo> cycleInFrameInfo;
685  const std::unique_ptr<LineInFrameInfo> lineInFrameInfo;
686  const std::unique_ptr<CycleInLineInfo> cycleInLineInfo;
687  const std::unique_ptr<MsxYPosInfo> msxYPosInfo;
688  const std::unique_ptr<MsxX256PosInfo> msxX256PosInfo;
689  const std::unique_ptr<MsxX512PosInfo> msxX512PosInfo;
690 
693  std::unique_ptr<Renderer> renderer;
694 
697  std::unique_ptr<VDPCmdEngine> cmdEngine;
698 
701  std::unique_ptr<SpriteChecker> spriteChecker;
702 
705  std::unique_ptr<VDPVRAM> vram;
706 
710  const RawFrame* externalVideo;
711 
717  const RawFrame* superimposing;
718 
721  VDPClock frameStartTime;
722 
725  IRQHelper irqVertical;
726 
729  IRQHelper irqHorizontal;
730 
733  EmuTime displayStartSyncTime;
734 
737  EmuTime vScanSyncTime;
738 
741  EmuTime hScanSyncTime;
742 
745  VdpVersion version;
746 
752  int frameCount;
753 
756  int displayStart;
757 
761  int horizontalScanOffset;
762 
766  int horizontalAdjust;
767 
770  byte controlRegs[32];
771 
776  int controlRegMask;
777 
784  byte controlValueMasks[32];
785 
789  int blinkCount;
790 
794  int vramPointer;
795 
798  word palette[16];
799 
802  bool isDisplayArea;
803 
809  bool palTiming;
810 
814  bool interlaced;
815 
820  byte statusReg0;
821 
827  byte statusReg1;
828 
833  byte statusReg2;
834 
837  bool blinkState;
838 
841  byte dataLatch;
842 
845  bool registerDataStored;
846 
849  bool paletteDataStored;
850 
858  byte cpuVramData;
859 
863  bool cpuVramReqIsRead;
864 
868  bool cpuExtendedVram;
869 
875  DisplayMode displayMode;
876 
881  bool displayEnabled;
882 
886  bool warningPrinted;
887 };
889 
890 
892 {
893 public:
894  AccessSlotCalculator(unsigned ticks_, const unsigned* tab, unsigned tabLen_)
895  : ticks(ticks_), tabLen(tabLen_)
896  {
897  // Search largest value that is smaller or equal to ticks.
898  // This could be outside the table boundaries (one element
899  // in front), but that's ok because it won't be dereferenced.
900  idx = std::upper_bound(tab, tab + tabLen, ticks) - 1;
901  assert((idx < tab) || (*idx <= ticks));
902  }
903  inline EmuDuration getNext(unsigned delta) {
904  assert(delta != 0);
905  assert(delta <= 136);
906  auto stop = ticks + delta;
907  do { ++idx; } while (*idx < stop);
908  auto duration = VDP::VDPClock::duration(*idx - ticks);
909  if (unlikely(*idx >= 1368)) idx -= tabLen;
910  ticks = *idx;
911  return duration;
912  }
913 private:
914  // Usually *idx == ticks, but in case the VDP switched mode
915  // in the middle of a command (e.g. enabled/disabled sprites)
916  // this may not be the case. So we need both vars.
917  const unsigned* idx;
918  unsigned ticks;
919  const unsigned tabLen;
920 };
921 
922 
923 } // namespace openmsx
924 
925 #endif