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"
6#include "TclCallback.hh"
8#include "IRQHelper.hh"
9#include "V9990CmdEngine.hh"
10#include "V9990DisplayTiming.hh"
11#include "V9990ModeEnum.hh"
12#include "V9990VRAM.hh"
13#include "SimpleDebuggable.hh"
14#include "Clock.hh"
15#include "narrow.hh"
16#include "openmsx.hh"
17#include "one_of.hh"
18#include "outer.hh"
19#include "serialize_meta.hh"
20#include "unreachable.hh"
21#include <array>
22#include <memory>
23#include <optional>
24
25namespace openmsx {
26
27class PostProcessor;
28class Display;
29class V9990Renderer;
30
34class V9990 final : public MSXDevice, private VideoSystemChangeListener
35{
36public:
37 explicit V9990(const DeviceConfig& config);
38 ~V9990() override;
39
40 // MSXDevice interface:
41 void powerUp(EmuTime::param time) override;
42 void reset(EmuTime::param time) override;
43 [[nodiscard]] byte readIO(word port, EmuTime::param time) override;
44 [[nodiscard]] byte peekIO(word port, EmuTime::param time) const override;
45 void writeIO(word port, byte value, EmuTime::param time) override;
46
51 [[nodiscard]] PostProcessor* getPostProcessor() const;
52
55 [[nodiscard]] inline V9990VRAM& getVRAM() {
56 return vram;
57 }
58
62 [[nodiscard]] inline bool isInterlaced() const {
63 return interlaced;
64 }
65
69 [[nodiscard]] inline bool isEvenOddEnabled() const {
70 return (regs[SCREEN_MODE_1] & 0x04) != 0;
71 }
72
76 [[nodiscard]] inline bool getEvenOdd() const {
77 return (status & 0x02) != 0;
78 }
79
85 [[nodiscard]] inline bool isDisplayEnabled() const {
86 return isDisplayArea && displayEnabled;
87 }
88
92 [[nodiscard]] inline bool spritesEnabled() const {
93 return !(regs[CONTROL] & 0x40);
94 }
95
101 [[nodiscard]] inline byte getPaletteOffset() const {
102 return (regs[PALETTE_CONTROL] & 0x0F);
103 }
104
113 byte r, g, b;
114 bool ys;
115 };
116 [[nodiscard]] GetPaletteResult getPalette(int index) const;
117
122 [[nodiscard]] inline int getUCTicksThisFrame(EmuTime::param time) const {
123 return narrow<int>(frameStartTime.getTicksTill_fast(time));
124 }
125
130 [[nodiscard]] inline bool isPalTiming() const {
131 return palTiming;
132 }
133
136 [[nodiscard]] inline bool isOverScan() const {
138 }
139
146 [[nodiscard]] inline bool isSuperimposing() const {
147 return superimposing;
148 }
149
151 void setExternalVideoSource(bool enable) {
152 externalVideoSource = enable;
153 }
154
159 [[nodiscard]] inline unsigned getCursorYOffset() const {
160 // TODO vertical set-adjust may or may not influence this,
161 // need to investigate that.
162 if (!isOverScan()) return 0;
163 return isPalTiming()
166 }
167
174 [[nodiscard]] static inline int UCtoX(int ticks, V9990DisplayMode mode) {
175 int x;
177 switch (mode) {
178 using enum V9990DisplayMode;
179 case P1: x = ticks / 8; break;
180 case P2: x = ticks / 4; break;
181 case B0: x = ticks /12; break;
182 case B1: x = ticks / 8; break;
183 case B2: x = ticks / 6; break;
184 case B3: x = ticks / 4; break;
185 case B4: x = ticks / 3; break;
186 case B5: x = 1; break;
187 case B6: x = 1; break;
188 case B7: x = ticks / 2; break;
189 default: x = 1;
190 }
191 return x;
192 }
193
196 [[nodiscard]] inline V9990DisplayMode getDisplayMode() const {
197 return mode;
198 }
199
203
211 [[nodiscard]] inline unsigned getColorDepth() const {
212 return regs[SCREEN_MODE_0] & 0x03;
213 }
214
218 [[nodiscard]] inline byte getBackDropColor() const {
219 return regs[BACK_DROP_COLOR];
220 }
221
224 [[nodiscard]] inline unsigned getScrollAX() const {
225 return regs[SCROLL_CONTROL_AX0] + 8 * regs[SCROLL_CONTROL_AX1];
226 }
227
230 [[nodiscard]] inline unsigned getScrollAY() const {
231 return regs[SCROLL_CONTROL_AY0] + 256 * scrollAYHigh;
232 }
233
236 [[nodiscard]] inline unsigned getScrollBX() const {
237 return regs[SCROLL_CONTROL_BX0] + 8 * regs[SCROLL_CONTROL_BX1];
238 }
239
242 [[nodiscard]] inline unsigned getScrollBY() const {
243 return regs[SCROLL_CONTROL_BY0] + 256 * scrollBYHigh;
244 }
245
248 [[nodiscard]] inline unsigned getRollMask(unsigned maxMask) const {
249 static std::array<unsigned, 4> rollMasks = {
250 0xFFFF, // no rolling (use maxMask)
251 0x00FF,
252 0x01FF,
253 0x00FF // TODO check this (undocumented)
254 };
255 unsigned t = regs[SCROLL_CONTROL_AY1] >> 6;
256 return t ? rollMasks[t] : maxMask;
257 }
258
261 [[nodiscard]] inline unsigned getImageWidth() const {
262 switch (regs[SCREEN_MODE_0] & 0xC0) {
263 case 0x00: // P1
264 return 256;
265 case 0x40: // P2
266 return 512;
267 case 0x80: // Bx
268 default: // standby TODO check this
269 return (256 << ((regs[SCREEN_MODE_0] & 0x0C) >> 2));
270 }
271 }
274 [[nodiscard]] inline unsigned getLineWidth() const {
275 switch (getDisplayMode()) {
276 using enum V9990DisplayMode;
277 case B0: return 213;
278 case P1: case B1: return 320;
279 case B2: return 426;
280 case P2: case B3: return 640;
281 case B4: return 853;
282 case B5: case B6: return 1; // not supported
283 case B7: return 1280;
284 default:
286 }
287 }
288
291 inline void cmdReady() {
292 raiseIRQ(CMD_IRQ);
293 }
294
297 [[nodiscard]] inline int getSpritePatternAddress(V9990DisplayMode m) const {
298 switch (m) {
300 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0E) << 14);
302 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0F) << 15);
303 default:
304 return 0;
305 }
306 }
307
310 [[nodiscard]] inline byte getSpritePaletteOffset() const {
311 return narrow_cast<byte>(regs[SPRITE_PALETTE_CONTROL] << 2);
312 }
313
316 [[nodiscard]] inline const V9990DisplayPeriod& getHorizontalTiming() const {
317 return *horTiming;
318 }
319
323 [[nodiscard]] inline int getLeftBorder() const {
324 return horTiming->blank + horTiming->border1 +
325 (((regs[DISPLAY_ADJUST] & 0x0F) ^ 7) - 8) * 8;
326 }
330 [[nodiscard]] inline int getRightBorder() const {
331 return getLeftBorder() + horTiming->display;
332 }
333
336 [[nodiscard]] inline const V9990DisplayPeriod& getVerticalTiming() const {
337 return *verTiming;
338 }
339
340 [[nodiscard]] inline int getTopBorder() const {
341 return verTiming->blank + verTiming->border1 +
342 (((regs[DISPLAY_ADJUST] >> 4) ^ 7) - 8);
343 }
344 [[nodiscard]] inline int getBottomBorder() const {
345 return getTopBorder() + verTiming->display;
346 }
347
348 [[nodiscard]] inline unsigned getPriorityControlX() const {
349 unsigned t = regs[PRIORITY_CONTROL] & 0x03;
350 return (t == 0) ? 256 : t << 6;
351 }
352 [[nodiscard]] inline unsigned getPriorityControlY() const {
353 unsigned t = regs[PRIORITY_CONTROL] & 0x0C;
354 return (t == 0) ? 256 : t << 4;
355 }
356
357 template<typename Archive>
358 void serialize(Archive& ar, unsigned version);
359
360private:
361 // VideoSystemChangeListener interface:
362 void preVideoSystemChange() noexcept override;
363 void postVideoSystemChange() noexcept override;
364
365 // Scheduler stuff
366 struct SyncBase : Schedulable {
367 explicit SyncBase(const V9990& v9990) : Schedulable(v9990.getScheduler()) {}
370 protected:
371 ~SyncBase() = default;
372 };
373
374 struct SyncVSync final : SyncBase {
375 using SyncBase::SyncBase;
376 void executeUntil(EmuTime::param time) override {
377 auto& v9990 = OUTER(V9990, syncVSync);
378 v9990.execVSync(time);
379 }
380 } syncVSync;
381
382 struct SyncDisplayStart final : SyncBase {
383 using SyncBase::SyncBase;
384 void executeUntil(EmuTime::param time) override {
385 auto& v9990 = OUTER(V9990, syncDisplayStart);
386 v9990.execDisplayStart(time);
387 }
388 } syncDisplayStart;
389
390 struct SyncVScan final : SyncBase {
391 using SyncBase::SyncBase;
392 void executeUntil(EmuTime::param time) override {
393 auto& v9990 = OUTER(V9990, syncVScan);
394 v9990.execVScan(time);
395 }
396 } syncVScan;
397
398 struct SyncHScan final : SyncBase {
399 using SyncBase::SyncBase;
400 void executeUntil(EmuTime::param /*time*/) override {
401 auto& v9990 = OUTER(V9990, syncHScan);
402 v9990.execHScan();
403 }
404 } syncHScan;
405
406 struct SyncSetMode final : SyncBase {
407 using SyncBase::SyncBase;
408 void executeUntil(EmuTime::param time) override {
409 auto& v9990 = OUTER(V9990, syncSetMode);
410 v9990.execSetMode(time);
411 }
412 } syncSetMode;
413
414 struct SyncCmdEnd final : SyncBase {
415 using SyncBase::SyncBase;
416 void executeUntil(EmuTime::param time) override {
417 auto& v9990 = OUTER(V9990, syncCmdEnd);
418 v9990.execCheckCmdEnd(time);
419 }
420 } syncCmdEnd;
421
422 void execVSync(EmuTime::param time);
423 void execDisplayStart(EmuTime::param time);
424 void execVScan(EmuTime::param time);
425 void execHScan();
426 void execSetMode(EmuTime::param time);
427 void execCheckCmdEnd(EmuTime::param time);
428
429 // --- types ------------------------------------------------------
430
433 enum IRQType : byte {
434 VER_IRQ = 1,
435 HOR_IRQ = 2,
436 CMD_IRQ = 4
437 };
438
441 enum PortId {
442 VRAM_DATA = 0,
443 PALETTE_DATA,
444 COMMAND_DATA,
445 REGISTER_DATA,
446 REGISTER_SELECT,
447 STATUS,
448 INTERRUPT_FLAG,
449 SYSTEM_CONTROL,
450 KANJI_ROM_0,
451 KANJI_ROM_1,
452 KANJI_ROM_2,
453 KANJI_ROM_3,
454 RESERVED_0,
455 RESERVED_1,
456 RESERVED_2,
457 RESERVED_3
458 };
459
462 enum RegisterId {
463 VRAM_WRITE_ADDRESS_0 = 0,
464 VRAM_WRITE_ADDRESS_1,
465 VRAM_WRITE_ADDRESS_2,
466 VRAM_READ_ADDRESS_0,
467 VRAM_READ_ADDRESS_1,
468 VRAM_READ_ADDRESS_2,
469 SCREEN_MODE_0,
470 SCREEN_MODE_1,
471 CONTROL,
472 INTERRUPT_0,
473 INTERRUPT_1,
474 INTERRUPT_2,
475 INTERRUPT_3,
476 PALETTE_CONTROL,
477 PALETTE_POINTER,
478 BACK_DROP_COLOR,
479 DISPLAY_ADJUST,
480 SCROLL_CONTROL_AY0,
481 SCROLL_CONTROL_AY1,
482 SCROLL_CONTROL_AX0,
483 SCROLL_CONTROL_AX1,
484 SCROLL_CONTROL_BY0,
485 SCROLL_CONTROL_BY1,
486 SCROLL_CONTROL_BX0,
487 SCROLL_CONTROL_BX1,
488 SPRITE_PATTERN_ADDRESS,
489 LCD_CONTROL,
490 PRIORITY_CONTROL,
491 SPRITE_PALETTE_CONTROL,
492 CMD_PARAM_SRC_ADDRESS_0 = 32,
493 CMD_PARAM_SRC_ADDRESS_1,
494 CMD_PARAM_SRC_ADDRESS_2,
495 CMD_PARAM_SRC_ADDRESS_3,
496 CMD_PARAM_DEST_ADDRESS_0,
497 CMD_PARAM_DEST_ADDRESS_1,
498 CMD_PARAM_DEST_ADDRESS_2,
499 CMD_PARAM_DEST_ADDRESS_3,
500 CMD_PARAM_SIZE_0,
501 CMD_PARAM_SIZE_1,
502 CMD_PARAM_SIZE_2,
503 CMD_PARAM_SIZE_3,
504 CMD_PARAM_ARGUMENT,
505 CMD_PARAM_LOGOP,
506 CMD_PARAM_WRITE_MASK_0,
507 CMD_PARAM_WRITE_MASK_1,
508 CMD_PARAM_FONT_COLOR_FC0,
509 CMD_PARAM_FONT_COLOR_FC1,
510 CMD_PARAM_FONT_COLOR_BC0,
511 CMD_PARAM_FONT_COLOR_BC1,
512 CMD_PARAM_OPCODE,
513 CMD_PARAM_BORDER_X_0,
514 CMD_PARAM_BORDER_X_1
515 };
516
517 // --- members ----------------------------------------------------
518
519 struct RegDebug final : SimpleDebuggable {
520 explicit RegDebug(const V9990& v9990);
521 [[nodiscard]] byte read(unsigned address) override;
522 void write(unsigned address, byte value, EmuTime::param time) override;
523 } v9990RegDebug;
524
525 struct PalDebug final : SimpleDebuggable {
526 explicit PalDebug(const V9990& v9990);
527 [[nodiscard]] byte read(unsigned address) override;
528 void write(unsigned address, byte value, EmuTime::param time) override;
529 } v9990PalDebug;
530
531 IRQHelper irq;
532
533 Display& display;
534
535 TclCallback invalidRegisterReadCallback;
536 TclCallback invalidRegisterWriteCallback;
537
540 V9990VRAM vram;
541 unsigned vramReadPtr, vramWritePtr;
542 byte vramReadBuffer;
543
546 V9990CmdEngine cmdEngine;
547
550 std::unique_ptr<V9990Renderer> renderer;
551
554 Clock<V9990DisplayTiming::UC_TICKS_PER_SECOND> frameStartTime;
555
558 EmuTime hScanSyncTime;
559
562 const V9990DisplayPeriod* horTiming;
563 const V9990DisplayPeriod* verTiming;
564
567 V9990DisplayMode mode;
568
571 std::array<byte, 0x100> palette;
572
575 byte status;
576
579 byte pendingIRQs = 0;
580
583 std::array<byte, 0x40> regs = {}; // fill with zero
584 byte regSelect;
585
588 bool palTiming{false};
589
593 bool interlaced{false};
594
597 bool isDisplayArea{false};
598
604 bool displayEnabled{false};
605
614 byte scrollAYHigh;
615 byte scrollBYHigh;
616
621 bool systemReset;
622
627 bool externalVideoSource{false};
628
632 bool superimposing{false};
633
634 // --- methods ----------------------------------------------------
635
636 void setHorizontalTiming();
637 void setVerticalTiming();
638
639 [[nodiscard]] V9990ColorMode getColorMode(byte pal_ctrl) const;
640
645 [[nodiscard]] inline unsigned getVRAMAddr(RegisterId base) const;
646
651 inline void setVRAMAddr(RegisterId base, unsigned addr);
652
658 [[nodiscard]] byte readRegister(byte reg, EmuTime::param time) const;
659
665 void writeRegister(byte reg, byte val, EmuTime::param time);
666
672 void writePaletteRegister(byte reg, byte val, EmuTime::param time);
673
676 void syncAtNextLine(SyncBase& type, EmuTime::param time) const;
677
681 void createRenderer(EmuTime::param time);
682
686 void frameStart(EmuTime::param time);
687
691 void raiseIRQ(IRQType irqType);
692
695 [[nodiscard]] V9990DisplayMode calcDisplayMode() const;
696 void setDisplayMode(V9990DisplayMode newMode);
697
702 void scheduleHscan(EmuTime::param time);
703
706 void scheduleCmdEnd(EmuTime::param time);
707};
709
710} // namespace openmsx
711
712#endif
TclObject t
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:36
Scheduler & getScheduler() const
Definition MSXDevice.cc:137
A post processor builds the frame that is displayed from the MSX frame, while applying effects such a...
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
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 clock ticks 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:17
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition V9990.hh:35
void reset(EmuTime::param time) override
This method is called on reset.
Definition V9990.cc:125
unsigned getCursorYOffset() const
In overscan mode the cursor position is still specified with 'normal' (non-overscan) y-coordinates.
Definition V9990.hh:159
int getTopBorder() const
Definition V9990.hh:340
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition V9990.cc:119
void serialize(Archive &ar, unsigned version)
Definition V9990.cc:892
bool isPalTiming() const
Is PAL timing active? This setting is fixed at start of frame.
Definition V9990.hh:130
int getSpritePatternAddress(V9990DisplayMode m) const
Return the sprite pattern table base address.
Definition V9990.hh:297
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:146
void cmdReady()
Command execution ready.
Definition V9990.hh:291
byte getPaletteOffset() const
Get palette offset.
Definition V9990.hh:101
byte getBackDropColor() const
Return the current back drop color.
Definition V9990.hh:218
int getUCTicksThisFrame(EmuTime::param time) const
Get the number of elapsed UC ticks in this frame.
Definition V9990.hh:122
V9990VRAM & getVRAM()
Obtain a reference to the V9990's VRAM.
Definition V9990.hh:55
bool isInterlaced() const
Get interlace status.
Definition V9990.hh:62
unsigned getLineWidth() const
Return the display width.
Definition V9990.hh:274
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition V9990.hh:69
unsigned getScrollBX() const
Returns the X scroll offset for screen B of P1 mode.
Definition V9990.hh:236
~V9990() override
Definition V9990.cc:105
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition V9990.hh:196
unsigned getImageWidth() const
Return the image width.
Definition V9990.hh:261
unsigned getPriorityControlX() const
Definition V9990.hh:348
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition V9990.hh:92
unsigned getColorDepth() const
Return the amount of bits per pixels.
Definition V9990.hh:211
void setExternalVideoSource(bool enable)
Is there an external video source available to superimpose on.
Definition V9990.hh:151
GetPaletteResult getPalette(int index) const
Definition V9990.cc:685
byte getSpritePaletteOffset() const
return sprite palette offset
Definition V9990.hh:310
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition V9990.hh:174
unsigned getPriorityControlY() const
Definition V9990.hh:352
int getBottomBorder() const
Definition V9990.hh:344
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition V9990.hh:224
int getLeftBorder() const
Get the number of VDP clock-ticks between the start of the line and the end of the left border.
Definition V9990.hh:323
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:275
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
Definition V9990.cc:110
bool isDisplayEnabled() const
Is the display enabled? Note this is simpler than the V99x8 version.
Definition V9990.hh:85
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition V9990.cc:222
unsigned getScrollBY() const
Returns the Y scroll offset for screen B of P1 mode.
Definition V9990.hh:242
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition V9990.hh:230
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition V9990.hh:248
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition V9990.hh:336
V9990ColorMode getColorMode() const
Return the current color mode.
Definition V9990.cc:804
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition V9990.hh:316
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:161
int getRightBorder() const
Get the number of VDP clock-ticks between the start of the line and the end of the right border.
Definition V9990.hh:330
bool isOverScan() const
Returns true iff in overscan mode.
Definition V9990.hh:136
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition V9990.hh:76
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
IntHelper< IRQSource > IRQHelper
Definition IRQHelper.hh:124
#define OUTER(type, member)
Definition outer.hh:42
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION)
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
#define UNREACHABLE