openMSX
V9990.hh
Go to the documentation of this file.
1 #ifndef V9990_HH
2 #define V9990_HH
3 
4 #include "MSXDevice.hh"
5 #include "Schedulable.hh"
7 #include "IRQHelper.hh"
8 #include "V9990CmdEngine.hh"
9 #include "V9990DisplayTiming.hh"
10 #include "V9990ModeEnum.hh"
11 #include "V9990VRAM.hh"
12 #include "SimpleDebuggable.hh"
13 #include "Clock.hh"
14 #include "serialize_meta.hh"
15 #include "openmsx.hh"
16 #include "one_of.hh"
17 #include "outer.hh"
18 #include "unreachable.hh"
19 #include <memory>
20 #include <optional>
21 
22 namespace openmsx {
23 
24 class PostProcessor;
25 class Display;
26 class V9990Renderer;
27 
31 class V9990 final : public MSXDevice, private VideoSystemChangeListener
32 {
33 public:
34  explicit V9990(const DeviceConfig& config);
35  ~V9990() override;
36 
37  // MSXDevice interface:
38  void powerUp(EmuTime::param time) override;
39  void reset(EmuTime::param time) override;
40  [[nodiscard]] byte readIO(word port, EmuTime::param time) override;
41  [[nodiscard]] byte peekIO(word port, EmuTime::param time) const override;
42  void writeIO(word port, byte value, EmuTime::param time) override;
43 
48  [[nodiscard]] PostProcessor* getPostProcessor() const;
49 
52  [[nodiscard]] inline V9990VRAM& getVRAM() {
53  return vram;
54  }
55 
59  [[nodiscard]] inline bool isInterlaced() const {
60  return interlaced;
61  }
62 
66  [[nodiscard]] inline bool isEvenOddEnabled() const {
67  return (regs[SCREEN_MODE_1] & 0x04) != 0;
68  }
69 
73  [[nodiscard]] inline bool getEvenOdd() const {
74  return (status & 0x02) != 0;
75  }
76 
82  [[nodiscard]] inline bool isDisplayEnabled() const {
83  return isDisplayArea && displayEnabled;
84  }
85 
89  [[nodiscard]] inline bool spritesEnabled() const {
90  return !(regs[CONTROL] & 0x40);
91  }
92 
98  [[nodiscard]] inline byte getPaletteOffset() const {
99  return (regs[PALETTE_CONTROL] & 0x0F);
100  }
101 
110  byte r, g, b;
111  bool ys;
112  };
113  [[nodiscard]] GetPaletteResult getPalette(int index) const;
114 
119  [[nodiscard]] inline int getUCTicksThisFrame(EmuTime::param time) const {
120  return frameStartTime.getTicksTill_fast(time);
121  }
122 
127  [[nodiscard]] inline bool isPalTiming() const {
128  return palTiming;
129  }
130 
133  [[nodiscard]] inline bool isOverScan() const {
134  return mode == one_of(B0, B2, B4);
135  }
136 
143  [[nodiscard]] inline bool isSuperimposing() const {
144  return superimposing;
145  }
146 
148  void setExternalVideoSource(bool enable) {
149  externalVideoSource = enable;
150  }
151 
156  [[nodiscard]] inline unsigned getCursorYOffset() const {
157  // TODO vertical set-adjust may or may not influence this,
158  // need to investigate that.
159  if (!isOverScan()) return 0;
160  return isPalTiming()
163  }
164 
171  [[nodiscard]] static inline int UCtoX(int ticks, V9990DisplayMode mode) {
172  int x;
174  switch (mode) {
175  case P1: x = ticks / 8; break;
176  case P2: x = ticks / 4; break;
177  case B0: x = ticks /12; break;
178  case B1: x = ticks / 8; break;
179  case B2: x = ticks / 6; break;
180  case B3: x = ticks / 4; break;
181  case B4: x = ticks / 3; break;
182  case B5: x = 1; break;
183  case B6: x = 1; break;
184  case B7: x = ticks / 2; break;
185  default: x = 1;
186  }
187  return x;
188  }
189 
192  [[nodiscard]] inline V9990DisplayMode getDisplayMode() const {
193  return mode;
194  }
195 
199 
207  [[nodiscard]] inline unsigned getColorDepth() const {
208  return regs[SCREEN_MODE_0] & 0x03;
209  }
210 
214  [[nodiscard]] inline byte getBackDropColor() const {
215  return regs[BACK_DROP_COLOR];
216  }
217 
220  [[nodiscard]] inline unsigned getScrollAX() const {
221  return regs[SCROLL_CONTROL_AX0] + 8 * regs[SCROLL_CONTROL_AX1];
222  }
223 
226  [[nodiscard]] inline unsigned getScrollAY() const {
227  return regs[SCROLL_CONTROL_AY0] + 256 * scrollAYHigh;
228  }
229 
232  [[nodiscard]] inline unsigned getScrollBX() const {
233  return regs[SCROLL_CONTROL_BX0] + 8 * regs[SCROLL_CONTROL_BX1];
234  }
235 
238  [[nodiscard]] inline unsigned getScrollBY() const {
239  return regs[SCROLL_CONTROL_BY0] + 256 * scrollBYHigh;
240  }
241 
244  [[nodiscard]] inline unsigned getRollMask(unsigned maxMask) const {
245  static unsigned rollMasks[4] = {
246  0xFFFF, // no rolling (use maxMask)
247  0x00FF,
248  0x01FF,
249  0x00FF // TODO check this (undocumented)
250  };
251  unsigned t = regs[SCROLL_CONTROL_AY1] >> 6;
252  return t ? rollMasks[t] : maxMask;
253  }
254 
257  [[nodiscard]] inline unsigned getImageWidth() const {
258  switch (regs[SCREEN_MODE_0] & 0xC0) {
259  case 0x00: // P1
260  return 256;
261  case 0x40: // P2
262  return 512;
263  case 0x80: // Bx
264  default: // standby TODO check this
265  return (256 << ((regs[SCREEN_MODE_0] & 0x0C) >> 2));
266  }
267  }
270  [[nodiscard]] inline unsigned getLineWidth() const {
271  switch (getDisplayMode()) {
272  case B0: return 213;
273  case P1: case B1: return 320;
274  case B2: return 426;
275  case P2: case B3: return 640;
276  case B4: return 853;
277  case B5: case B6: return 1; // not supported
278  case B7: return 1280;
279  default:
280  UNREACHABLE; return 0;
281  }
282  }
283 
286  inline void cmdReady() {
287  raiseIRQ(CMD_IRQ);
288  }
289 
292  [[nodiscard]] inline int getSpritePatternAddress(V9990DisplayMode m) const {
293  switch (m) {
294  case P1:
295  return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0E) << 14);
296  case P2:
297  return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0F) << 15);
298  default:
299  return 0;
300  }
301  }
302 
305  [[nodiscard]] inline byte getSpritePaletteOffset() const {
306  return regs[SPRITE_PALETTE_CONTROL] << 2;
307  }
308 
311  [[nodiscard]] inline const V9990DisplayPeriod& getHorizontalTiming() const {
312  return *horTiming;
313  }
314 
318  [[nodiscard]] inline int getLeftBorder() const {
319  return horTiming->blank + horTiming->border1 +
320  (((regs[DISPLAY_ADJUST] & 0x0F) ^ 7) - 8) * 8;
321  }
325  [[nodiscard]] inline int getRightBorder() const {
326  return getLeftBorder() + horTiming->display;
327  }
328 
331  [[nodiscard]] inline const V9990DisplayPeriod& getVerticalTiming() const {
332  return *verTiming;
333  }
334 
335  [[nodiscard]] inline int getTopBorder() const {
336  return verTiming->blank + verTiming->border1 +
337  (((regs[DISPLAY_ADJUST] >> 4) ^ 7) - 8);
338  }
339  [[nodiscard]] inline int getBottomBorder() const {
340  return getTopBorder() + verTiming->display;
341  }
342 
343  [[nodiscard]] inline unsigned getPriorityControlX() const {
344  unsigned t = regs[PRIORITY_CONTROL] & 0x03;
345  return (t == 0) ? 256 : t << 6;
346  }
347  [[nodiscard]] inline unsigned getPriorityControlY() const {
348  unsigned t = regs[PRIORITY_CONTROL] & 0x0C;
349  return (t == 0) ? 256 : t << 4;
350  }
351 
352  template<typename Archive>
353  void serialize(Archive& ar, unsigned version);
354 
355 private:
356  // VideoSystemChangeListener interface:
357  void preVideoSystemChange() noexcept override;
358  void postVideoSystemChange() noexcept override;
359 
360  // Scheduler stuff
361  struct SyncBase : Schedulable {
362  explicit SyncBase(V9990& v9990) : Schedulable(v9990.getScheduler()) {}
365  protected:
366  ~SyncBase() = default;
367  };
368 
369  struct SyncVSync final : SyncBase {
370  explicit SyncVSync(V9990& v9990) : SyncBase(v9990) {}
371  void executeUntil(EmuTime::param time) override {
372  auto& v9990 = OUTER(V9990, syncVSync);
373  v9990.execVSync(time);
374  }
375  } syncVSync;
376 
377  struct SyncDisplayStart final : SyncBase {
378  explicit SyncDisplayStart(V9990& v9990) : SyncBase(v9990) {}
379  void executeUntil(EmuTime::param time) override {
380  auto& v9990 = OUTER(V9990, syncDisplayStart);
381  v9990.execDisplayStart(time);
382  }
383  } syncDisplayStart;
384 
385  struct SyncVScan final : SyncBase {
386  explicit SyncVScan(V9990& v9990) : SyncBase(v9990) {}
387  void executeUntil(EmuTime::param time) override {
388  auto& v9990 = OUTER(V9990, syncVScan);
389  v9990.execVScan(time);
390  }
391  } syncVScan;
392 
393  struct SyncHScan final : SyncBase {
394  explicit SyncHScan(V9990& v9990) : SyncBase(v9990) {}
395  void executeUntil(EmuTime::param /*time*/) override {
396  auto& v9990 = OUTER(V9990, syncHScan);
397  v9990.execHScan();
398  }
399  } syncHScan;
400 
401  struct SyncSetMode final : SyncBase {
402  explicit SyncSetMode(V9990& v9990) : SyncBase(v9990) {}
403  void executeUntil(EmuTime::param time) override {
404  auto& v9990 = OUTER(V9990, syncSetMode);
405  v9990.execSetMode(time);
406  }
407  } syncSetMode;
408 
409  struct SyncCmdEnd final : SyncBase {
410  explicit SyncCmdEnd(V9990& v9990) : SyncBase(v9990) {}
411  void executeUntil(EmuTime::param time) override {
412  auto& v9990 = OUTER(V9990, syncCmdEnd);
413  v9990.execCheckCmdEnd(time);
414  }
415  } syncCmdEnd;
416 
417  void execVSync(EmuTime::param time);
418  void execDisplayStart(EmuTime::param time);
419  void execVScan(EmuTime::param time);
420  void execHScan();
421  void execSetMode(EmuTime::param time);
422  void execCheckCmdEnd(EmuTime::param time);
423 
424  // --- types ------------------------------------------------------
425 
428  enum IRQType {
429  VER_IRQ = 1,
430  HOR_IRQ = 2,
431  CMD_IRQ = 4
432  };
433 
436  enum PortId {
437  VRAM_DATA = 0,
438  PALETTE_DATA,
439  COMMAND_DATA,
440  REGISTER_DATA,
441  REGISTER_SELECT,
442  STATUS,
443  INTERRUPT_FLAG,
444  SYSTEM_CONTROL,
445  KANJI_ROM_0,
446  KANJI_ROM_1,
447  KANJI_ROM_2,
448  KANJI_ROM_3,
449  RESERVED_0,
450  RESERVED_1,
451  RESERVED_2,
452  RESERVED_3
453  };
454 
457  enum RegisterId {
458  VRAM_WRITE_ADDRESS_0 = 0,
459  VRAM_WRITE_ADDRESS_1,
460  VRAM_WRITE_ADDRESS_2,
461  VRAM_READ_ADDRESS_0,
462  VRAM_READ_ADDRESS_1,
463  VRAM_READ_ADDRESS_2,
464  SCREEN_MODE_0,
465  SCREEN_MODE_1,
466  CONTROL,
467  INTERRUPT_0,
468  INTERRUPT_1,
469  INTERRUPT_2,
470  INTERRUPT_3,
471  PALETTE_CONTROL,
472  PALETTE_POINTER,
473  BACK_DROP_COLOR,
474  DISPLAY_ADJUST,
475  SCROLL_CONTROL_AY0,
476  SCROLL_CONTROL_AY1,
477  SCROLL_CONTROL_AX0,
478  SCROLL_CONTROL_AX1,
479  SCROLL_CONTROL_BY0,
480  SCROLL_CONTROL_BY1,
481  SCROLL_CONTROL_BX0,
482  SCROLL_CONTROL_BX1,
483  SPRITE_PATTERN_ADDRESS,
484  LCD_CONTROL,
485  PRIORITY_CONTROL,
486  SPRITE_PALETTE_CONTROL,
487  CMD_PARAM_SRC_ADDRESS_0 = 32,
488  CMD_PARAM_SRC_ADDRESS_1,
489  CMD_PARAM_SRC_ADDRESS_2,
490  CMD_PARAM_SRC_ADDRESS_3,
491  CMD_PARAM_DEST_ADDRESS_0,
492  CMD_PARAM_DEST_ADDRESS_1,
493  CMD_PARAM_DEST_ADDRESS_2,
494  CMD_PARAM_DEST_ADDRESS_3,
495  CMD_PARAM_SIZE_0,
496  CMD_PARAM_SIZE_1,
497  CMD_PARAM_SIZE_2,
498  CMD_PARAM_SIZE_3,
499  CMD_PARAM_ARGUMENT,
500  CMD_PARAM_LOGOP,
501  CMD_PARAM_WRITE_MASK_0,
502  CMD_PARAM_WRITE_MASK_1,
503  CMD_PARAM_FONT_COLOR_FC0,
504  CMD_PARAM_FONT_COLOR_FC1,
505  CMD_PARAM_FONT_COLOR_BC0,
506  CMD_PARAM_FONT_COLOR_BC1,
507  CMD_PARAM_OPCODE,
508  CMD_PARAM_BORDER_X_0,
509  CMD_PARAM_BORDER_X_1
510  };
511 
512  // --- members ----------------------------------------------------
513 
514  struct RegDebug final : SimpleDebuggable {
515  explicit RegDebug(V9990& v9990);
516  [[nodiscard]] byte read(unsigned address) override;
517  void write(unsigned address, byte value, EmuTime::param time) override;
518  } v9990RegDebug;
519 
520  struct PalDebug final : SimpleDebuggable {
521  explicit PalDebug(V9990& v9990);
522  [[nodiscard]] byte read(unsigned address) override;
523  void write(unsigned address, byte value, EmuTime::param time) override;
524  } v9990PalDebug;
525 
526  IRQHelper irq;
527 
528  Display& display;
529 
532  V9990VRAM vram;
533  unsigned vramReadPtr, vramWritePtr;
534  byte vramReadBuffer;
535 
538  V9990CmdEngine cmdEngine;
539 
542  std::unique_ptr<V9990Renderer> renderer;
543 
546  Clock<V9990DisplayTiming::UC_TICKS_PER_SECOND> frameStartTime;
547 
550  EmuTime hScanSyncTime;
551 
554  const V9990DisplayPeriod* horTiming;
555  const V9990DisplayPeriod* verTiming;
556 
559  V9990DisplayMode mode;
560 
563  byte palette[0x100];
564 
567  byte status;
568 
571  byte pendingIRQs;
572 
575  byte regs[0x40];
576  byte regSelect;
577 
580  bool palTiming;
581 
585  bool interlaced;
586 
589  bool isDisplayArea;
590 
596  bool displayEnabled;
597 
606  byte scrollAYHigh;
607  byte scrollBYHigh;
608 
613  bool systemReset;
614 
619  bool externalVideoSource;
620 
624  bool superimposing;
625 
626  // --- methods ----------------------------------------------------
627 
628  void setHorizontalTiming();
629  void setVerticalTiming();
630 
631  [[nodiscard]] V9990ColorMode getColorMode(byte pal_ctrl) const;
632 
637  [[nodiscard]] inline unsigned getVRAMAddr(RegisterId base) const;
638 
643  inline void setVRAMAddr(RegisterId base, unsigned addr);
644 
650  [[nodiscard]] byte readRegister(byte reg, EmuTime::param time) const;
651 
657  void writeRegister(byte reg, byte val, EmuTime::param time);
658 
664  void writePaletteRegister(byte reg, byte val, EmuTime::param time);
665 
668  void syncAtNextLine(SyncBase& type, EmuTime::param time);
669 
673  void createRenderer(EmuTime::param time);
674 
678  void frameStart(EmuTime::param time);
679 
683  void raiseIRQ(IRQType irqType);
684 
687  [[nodiscard]] V9990DisplayMode calcDisplayMode() const;
688  void setDisplayMode(V9990DisplayMode newMode);
689 
694  void scheduleHscan(EmuTime::param time);
695 
698  void scheduleCmdEnd(EmuTime::param time);
699 };
701 
702 } // namespace openmsx
703 
704 #endif
TclObject t
Definition: one_of.hh:7
constexpr unsigned getTicksTill_fast(EmuTime::param e) const
Same as above, only faster, Though the time interval may not be too large.
Definition: Clock.hh:70
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
Scheduler & getScheduler() const
Definition: MSXDevice.cc:138
Abstract base class for post processors.
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
virtual byte read(unsigned address, EmuTime::param time)
void write(unsigned address, byte value) override
static constexpr int UC_TICKS_PER_LINE
The number of clockticks per line is independent of the crystal used or the display mode (NTSC/PAL)
static constexpr auto displayNTSC_MCLK
NTSC display timing, when using MCLK: Normal display mode with borders.
static constexpr auto displayPAL_MCLK
PAL display timing, when using MCLK: Normal display mode with borders.
Video RAM for the V9990.
Definition: V9990VRAM.hh:16
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:32
void reset(EmuTime::param time) override
This method is called on reset.
Definition: V9990.cc:122
unsigned getCursorYOffset() const
In overscan mode the cursor position is still specified with 'normal' (non-overscan) y-coordinates.
Definition: V9990.hh:156
int getTopBorder() const
Definition: V9990.hh:335
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition: V9990.cc:116
void serialize(Archive &ar, unsigned version)
Definition: V9990.cc:882
bool isPalTiming() const
Is PAL timing active? This setting is fixed at start of frame.
Definition: V9990.hh:127
int getSpritePatternAddress(V9990DisplayMode m) const
Return the sprite pattern table base address.
Definition: V9990.hh:292
bool isSuperimposing() const
Should this frame be superimposed? This is a combination of bit 5 (YSE) in R#8 and the presence of an...
Definition: V9990.hh:143
void cmdReady()
Command execution ready.
Definition: V9990.hh:286
byte getPaletteOffset() const
Get palette offset.
Definition: V9990.hh:98
byte getBackDropColor() const
Return the current back drop color.
Definition: V9990.hh:214
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition: V9990.hh:331
int getUCTicksThisFrame(EmuTime::param time) const
Get the number of elapsed UC ticks in this frame.
Definition: V9990.hh:119
bool isInterlaced() const
Get interlace status.
Definition: V9990.hh:59
unsigned getLineWidth() const
Return the display width.
Definition: V9990.hh:270
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition: V9990.hh:66
unsigned getScrollBX() const
Returns the X scroll offset for screen B of P1 mode.
Definition: V9990.hh:232
~V9990() override
Definition: V9990.cc:102
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition: V9990.hh:192
unsigned getImageWidth() const
Return the image width.
Definition: V9990.hh:257
V9990VRAM & getVRAM()
Obtain a reference to the V9990's VRAM.
Definition: V9990.hh:52
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition: V9990.hh:311
unsigned getPriorityControlX() const
Definition: V9990.hh:343
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition: V9990.hh:89
unsigned getColorDepth() const
Return the amount of bits per pixels.
Definition: V9990.hh:207
void setExternalVideoSource(bool enable)
Is there an external video source available to superimpose on.
Definition: V9990.hh:148
GetPaletteResult getPalette(int index) const
Definition: V9990.cc:679
byte getSpritePaletteOffset() const
return sprite palette offset
Definition: V9990.hh:305
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition: V9990.hh:171
unsigned getPriorityControlY() const
Definition: V9990.hh:347
int getBottomBorder() const
Definition: V9990.hh:339
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:220
int getLeftBorder() const
Get the number of VDP clockticks between the start of the line and the end of the left border.
Definition: V9990.hh:318
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition: V9990.cc:272
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
Definition: V9990.cc:107
bool isDisplayEnabled() const
Is the display enabled? Note this is simpler than the V99x8 version.
Definition: V9990.hh:82
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: V9990.cc:219
unsigned getScrollBY() const
Returns the Y scroll offset for screen B of P1 mode.
Definition: V9990.hh:238
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:226
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition: V9990.hh:244
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:796
V9990(const DeviceConfig &config)
Definition: V9990.cc:53
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: V9990.cc:158
int getRightBorder() const
Get the number of VDP clockticks between the start of the line and the end of the right border.
Definition: V9990.hh:325
bool isOverScan() const
Returns true iff in overscan mode.
Definition: V9990.hh:133
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition: V9990.hh:73
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:118
IntHelper< IRQSource > IRQHelper
Definition: IRQHelper.hh:122
#define OUTER(type, member)
Definition: outer.hh:41
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
Get palette entry.
Definition: V9990.hh:109
#define UNREACHABLE
Definition: unreachable.hh:38