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"
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
22namespace openmsx {
23
24class PostProcessor;
25class Display;
26class V9990Renderer;
27
31class V9990 final : public MSXDevice, private VideoSystemChangeListener
32{
33public:
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
355private:
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
int getUCTicksThisFrame(EmuTime::param time) const
Get the number of elapsed UC ticks in this frame.
Definition: V9990.hh:119
V9990VRAM & getVRAM()
Obtain a reference to the V9990's VRAM.
Definition: V9990.hh:52
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
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
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition: V9990.hh:331
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:796
V9990(const DeviceConfig &config)
Definition: V9990.cc:53
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition: V9990.hh:311
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:127
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