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 "narrow.hh"
15#include "openmsx.hh"
16#include "one_of.hh"
17#include "outer.hh"
18#include "serialize_meta.hh"
19#include "unreachable.hh"
20#include <array>
21#include <memory>
22#include <optional>
23
24namespace openmsx {
25
26class PostProcessor;
27class Display;
28class V9990Renderer;
29
33class V9990 final : public MSXDevice, private VideoSystemChangeListener
34{
35public:
36 explicit V9990(const DeviceConfig& config);
37 ~V9990() override;
38
39 // MSXDevice interface:
40 void powerUp(EmuTime::param time) override;
41 void reset(EmuTime::param time) override;
42 [[nodiscard]] byte readIO(word port, EmuTime::param time) override;
43 [[nodiscard]] byte peekIO(word port, EmuTime::param time) const override;
44 void writeIO(word port, byte value, EmuTime::param time) override;
45
50 [[nodiscard]] PostProcessor* getPostProcessor() const;
51
54 [[nodiscard]] inline V9990VRAM& getVRAM() {
55 return vram;
56 }
57
61 [[nodiscard]] inline bool isInterlaced() const {
62 return interlaced;
63 }
64
68 [[nodiscard]] inline bool isEvenOddEnabled() const {
69 return (regs[SCREEN_MODE_1] & 0x04) != 0;
70 }
71
75 [[nodiscard]] inline bool getEvenOdd() const {
76 return (status & 0x02) != 0;
77 }
78
84 [[nodiscard]] inline bool isDisplayEnabled() const {
85 return isDisplayArea && displayEnabled;
86 }
87
91 [[nodiscard]] inline bool spritesEnabled() const {
92 return !(regs[CONTROL] & 0x40);
93 }
94
100 [[nodiscard]] inline byte getPaletteOffset() const {
101 return (regs[PALETTE_CONTROL] & 0x0F);
102 }
103
112 byte r, g, b;
113 bool ys;
114 };
115 [[nodiscard]] GetPaletteResult getPalette(int index) const;
116
121 [[nodiscard]] inline int getUCTicksThisFrame(EmuTime::param time) const {
122 return narrow<int>(frameStartTime.getTicksTill_fast(time));
123 }
124
129 [[nodiscard]] inline bool isPalTiming() const {
130 return palTiming;
131 }
132
135 [[nodiscard]] inline bool isOverScan() const {
136 return mode == one_of(B0, B2, B4);
137 }
138
145 [[nodiscard]] inline bool isSuperimposing() const {
146 return superimposing;
147 }
148
150 void setExternalVideoSource(bool enable) {
151 externalVideoSource = enable;
152 }
153
158 [[nodiscard]] inline unsigned getCursorYOffset() const {
159 // TODO vertical set-adjust may or may not influence this,
160 // need to investigate that.
161 if (!isOverScan()) return 0;
162 return isPalTiming()
165 }
166
173 [[nodiscard]] static inline int UCtoX(int ticks, V9990DisplayMode mode) {
174 int x;
176 switch (mode) {
177 case P1: x = ticks / 8; break;
178 case P2: x = ticks / 4; break;
179 case B0: x = ticks /12; break;
180 case B1: x = ticks / 8; break;
181 case B2: x = ticks / 6; break;
182 case B3: x = ticks / 4; break;
183 case B4: x = ticks / 3; break;
184 case B5: x = 1; break;
185 case B6: x = 1; break;
186 case B7: x = ticks / 2; break;
187 default: x = 1;
188 }
189 return x;
190 }
191
194 [[nodiscard]] inline V9990DisplayMode getDisplayMode() const {
195 return mode;
196 }
197
201
209 [[nodiscard]] inline unsigned getColorDepth() const {
210 return regs[SCREEN_MODE_0] & 0x03;
211 }
212
216 [[nodiscard]] inline byte getBackDropColor() const {
217 return regs[BACK_DROP_COLOR];
218 }
219
222 [[nodiscard]] inline unsigned getScrollAX() const {
223 return regs[SCROLL_CONTROL_AX0] + 8 * regs[SCROLL_CONTROL_AX1];
224 }
225
228 [[nodiscard]] inline unsigned getScrollAY() const {
229 return regs[SCROLL_CONTROL_AY0] + 256 * scrollAYHigh;
230 }
231
234 [[nodiscard]] inline unsigned getScrollBX() const {
235 return regs[SCROLL_CONTROL_BX0] + 8 * regs[SCROLL_CONTROL_BX1];
236 }
237
240 [[nodiscard]] inline unsigned getScrollBY() const {
241 return regs[SCROLL_CONTROL_BY0] + 256 * scrollBYHigh;
242 }
243
246 [[nodiscard]] inline unsigned getRollMask(unsigned maxMask) const {
247 static std::array<unsigned, 4> rollMasks = {
248 0xFFFF, // no rolling (use maxMask)
249 0x00FF,
250 0x01FF,
251 0x00FF // TODO check this (undocumented)
252 };
253 unsigned t = regs[SCROLL_CONTROL_AY1] >> 6;
254 return t ? rollMasks[t] : maxMask;
255 }
256
259 [[nodiscard]] inline unsigned getImageWidth() const {
260 switch (regs[SCREEN_MODE_0] & 0xC0) {
261 case 0x00: // P1
262 return 256;
263 case 0x40: // P2
264 return 512;
265 case 0x80: // Bx
266 default: // standby TODO check this
267 return (256 << ((regs[SCREEN_MODE_0] & 0x0C) >> 2));
268 }
269 }
272 [[nodiscard]] inline unsigned getLineWidth() const {
273 switch (getDisplayMode()) {
274 case B0: return 213;
275 case P1: case B1: return 320;
276 case B2: return 426;
277 case P2: case B3: return 640;
278 case B4: return 853;
279 case B5: case B6: return 1; // not supported
280 case B7: return 1280;
281 default:
282 UNREACHABLE; return 0;
283 }
284 }
285
288 inline void cmdReady() {
289 raiseIRQ(CMD_IRQ);
290 }
291
294 [[nodiscard]] inline int getSpritePatternAddress(V9990DisplayMode m) const {
295 switch (m) {
296 case P1:
297 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0E) << 14);
298 case P2:
299 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0F) << 15);
300 default:
301 return 0;
302 }
303 }
304
307 [[nodiscard]] inline byte getSpritePaletteOffset() const {
308 return regs[SPRITE_PALETTE_CONTROL] << 2;
309 }
310
313 [[nodiscard]] inline const V9990DisplayPeriod& getHorizontalTiming() const {
314 return *horTiming;
315 }
316
320 [[nodiscard]] inline int getLeftBorder() const {
321 return horTiming->blank + horTiming->border1 +
322 (((regs[DISPLAY_ADJUST] & 0x0F) ^ 7) - 8) * 8;
323 }
327 [[nodiscard]] inline int getRightBorder() const {
328 return getLeftBorder() + horTiming->display;
329 }
330
333 [[nodiscard]] inline const V9990DisplayPeriod& getVerticalTiming() const {
334 return *verTiming;
335 }
336
337 [[nodiscard]] inline int getTopBorder() const {
338 return verTiming->blank + verTiming->border1 +
339 (((regs[DISPLAY_ADJUST] >> 4) ^ 7) - 8);
340 }
341 [[nodiscard]] inline int getBottomBorder() const {
342 return getTopBorder() + verTiming->display;
343 }
344
345 [[nodiscard]] inline unsigned getPriorityControlX() const {
346 unsigned t = regs[PRIORITY_CONTROL] & 0x03;
347 return (t == 0) ? 256 : t << 6;
348 }
349 [[nodiscard]] inline unsigned getPriorityControlY() const {
350 unsigned t = regs[PRIORITY_CONTROL] & 0x0C;
351 return (t == 0) ? 256 : t << 4;
352 }
353
354 template<typename Archive>
355 void serialize(Archive& ar, unsigned version);
356
357private:
358 // VideoSystemChangeListener interface:
359 void preVideoSystemChange() noexcept override;
360 void postVideoSystemChange() noexcept override;
361
362 // Scheduler stuff
363 struct SyncBase : Schedulable {
364 explicit SyncBase(V9990& v9990) : Schedulable(v9990.getScheduler()) {}
367 protected:
368 ~SyncBase() = default;
369 };
370
371 struct SyncVSync final : SyncBase {
372 explicit SyncVSync(V9990& v9990) : SyncBase(v9990) {}
373 void executeUntil(EmuTime::param time) override {
374 auto& v9990 = OUTER(V9990, syncVSync);
375 v9990.execVSync(time);
376 }
377 } syncVSync;
378
379 struct SyncDisplayStart final : SyncBase {
380 explicit SyncDisplayStart(V9990& v9990) : SyncBase(v9990) {}
381 void executeUntil(EmuTime::param time) override {
382 auto& v9990 = OUTER(V9990, syncDisplayStart);
383 v9990.execDisplayStart(time);
384 }
385 } syncDisplayStart;
386
387 struct SyncVScan final : SyncBase {
388 explicit SyncVScan(V9990& v9990) : SyncBase(v9990) {}
389 void executeUntil(EmuTime::param time) override {
390 auto& v9990 = OUTER(V9990, syncVScan);
391 v9990.execVScan(time);
392 }
393 } syncVScan;
394
395 struct SyncHScan final : SyncBase {
396 explicit SyncHScan(V9990& v9990) : SyncBase(v9990) {}
397 void executeUntil(EmuTime::param /*time*/) override {
398 auto& v9990 = OUTER(V9990, syncHScan);
399 v9990.execHScan();
400 }
401 } syncHScan;
402
403 struct SyncSetMode final : SyncBase {
404 explicit SyncSetMode(V9990& v9990) : SyncBase(v9990) {}
405 void executeUntil(EmuTime::param time) override {
406 auto& v9990 = OUTER(V9990, syncSetMode);
407 v9990.execSetMode(time);
408 }
409 } syncSetMode;
410
411 struct SyncCmdEnd final : SyncBase {
412 explicit SyncCmdEnd(V9990& v9990) : SyncBase(v9990) {}
413 void executeUntil(EmuTime::param time) override {
414 auto& v9990 = OUTER(V9990, syncCmdEnd);
415 v9990.execCheckCmdEnd(time);
416 }
417 } syncCmdEnd;
418
419 void execVSync(EmuTime::param time);
420 void execDisplayStart(EmuTime::param time);
421 void execVScan(EmuTime::param time);
422 void execHScan();
423 void execSetMode(EmuTime::param time);
424 void execCheckCmdEnd(EmuTime::param time);
425
426 // --- types ------------------------------------------------------
427
430 enum IRQType {
431 VER_IRQ = 1,
432 HOR_IRQ = 2,
433 CMD_IRQ = 4
434 };
435
438 enum PortId {
439 VRAM_DATA = 0,
440 PALETTE_DATA,
441 COMMAND_DATA,
442 REGISTER_DATA,
443 REGISTER_SELECT,
444 STATUS,
445 INTERRUPT_FLAG,
446 SYSTEM_CONTROL,
447 KANJI_ROM_0,
448 KANJI_ROM_1,
449 KANJI_ROM_2,
450 KANJI_ROM_3,
451 RESERVED_0,
452 RESERVED_1,
453 RESERVED_2,
454 RESERVED_3
455 };
456
459 enum RegisterId {
460 VRAM_WRITE_ADDRESS_0 = 0,
461 VRAM_WRITE_ADDRESS_1,
462 VRAM_WRITE_ADDRESS_2,
463 VRAM_READ_ADDRESS_0,
464 VRAM_READ_ADDRESS_1,
465 VRAM_READ_ADDRESS_2,
466 SCREEN_MODE_0,
467 SCREEN_MODE_1,
468 CONTROL,
469 INTERRUPT_0,
470 INTERRUPT_1,
471 INTERRUPT_2,
472 INTERRUPT_3,
473 PALETTE_CONTROL,
474 PALETTE_POINTER,
475 BACK_DROP_COLOR,
476 DISPLAY_ADJUST,
477 SCROLL_CONTROL_AY0,
478 SCROLL_CONTROL_AY1,
479 SCROLL_CONTROL_AX0,
480 SCROLL_CONTROL_AX1,
481 SCROLL_CONTROL_BY0,
482 SCROLL_CONTROL_BY1,
483 SCROLL_CONTROL_BX0,
484 SCROLL_CONTROL_BX1,
485 SPRITE_PATTERN_ADDRESS,
486 LCD_CONTROL,
487 PRIORITY_CONTROL,
488 SPRITE_PALETTE_CONTROL,
489 CMD_PARAM_SRC_ADDRESS_0 = 32,
490 CMD_PARAM_SRC_ADDRESS_1,
491 CMD_PARAM_SRC_ADDRESS_2,
492 CMD_PARAM_SRC_ADDRESS_3,
493 CMD_PARAM_DEST_ADDRESS_0,
494 CMD_PARAM_DEST_ADDRESS_1,
495 CMD_PARAM_DEST_ADDRESS_2,
496 CMD_PARAM_DEST_ADDRESS_3,
497 CMD_PARAM_SIZE_0,
498 CMD_PARAM_SIZE_1,
499 CMD_PARAM_SIZE_2,
500 CMD_PARAM_SIZE_3,
501 CMD_PARAM_ARGUMENT,
502 CMD_PARAM_LOGOP,
503 CMD_PARAM_WRITE_MASK_0,
504 CMD_PARAM_WRITE_MASK_1,
505 CMD_PARAM_FONT_COLOR_FC0,
506 CMD_PARAM_FONT_COLOR_FC1,
507 CMD_PARAM_FONT_COLOR_BC0,
508 CMD_PARAM_FONT_COLOR_BC1,
509 CMD_PARAM_OPCODE,
510 CMD_PARAM_BORDER_X_0,
511 CMD_PARAM_BORDER_X_1
512 };
513
514 // --- members ----------------------------------------------------
515
516 struct RegDebug final : SimpleDebuggable {
517 explicit RegDebug(V9990& v9990);
518 [[nodiscard]] byte read(unsigned address) override;
519 void write(unsigned address, byte value, EmuTime::param time) override;
520 } v9990RegDebug;
521
522 struct PalDebug final : SimpleDebuggable {
523 explicit PalDebug(V9990& v9990);
524 [[nodiscard]] byte read(unsigned address) override;
525 void write(unsigned address, byte value, EmuTime::param time) override;
526 } v9990PalDebug;
527
528 IRQHelper irq;
529
530 Display& display;
531
534 V9990VRAM vram;
535 unsigned vramReadPtr, vramWritePtr;
536 byte vramReadBuffer;
537
540 V9990CmdEngine cmdEngine;
541
544 std::unique_ptr<V9990Renderer> renderer;
545
548 Clock<V9990DisplayTiming::UC_TICKS_PER_SECOND> frameStartTime;
549
552 EmuTime hScanSyncTime;
553
556 const V9990DisplayPeriod* horTiming;
557 const V9990DisplayPeriod* verTiming;
558
561 V9990DisplayMode mode;
562
565 std::array<byte, 0x100> palette;
566
569 byte status;
570
573 byte pendingIRQs = 0;
574
577 std::array<byte, 0x40> regs = {}; // fill with zero
578 byte regSelect;
579
582 bool palTiming{false};
583
587 bool interlaced{false};
588
591 bool isDisplayArea{false};
592
598 bool displayEnabled{false};
599
608 byte scrollAYHigh;
609 byte scrollBYHigh;
610
615 bool systemReset;
616
621 bool externalVideoSource{false};
622
626 bool superimposing{false};
627
628 // --- methods ----------------------------------------------------
629
630 void setHorizontalTiming();
631 void setVerticalTiming();
632
633 [[nodiscard]] V9990ColorMode getColorMode(byte pal_ctrl) const;
634
639 [[nodiscard]] inline unsigned getVRAMAddr(RegisterId base) const;
640
645 inline void setVRAMAddr(RegisterId base, unsigned addr);
646
652 [[nodiscard]] byte readRegister(byte reg, EmuTime::param time) const;
653
659 void writeRegister(byte reg, byte val, EmuTime::param time);
660
666 void writePaletteRegister(byte reg, byte val, EmuTime::param time);
667
670 void syncAtNextLine(SyncBase& type, EmuTime::param time);
671
675 void createRenderer(EmuTime::param time);
676
680 void frameStart(EmuTime::param time);
681
685 void raiseIRQ(IRQType irqType);
686
689 [[nodiscard]] V9990DisplayMode calcDisplayMode() const;
690 void setDisplayMode(V9990DisplayMode newMode);
691
696 void scheduleHscan(EmuTime::param time);
697
700 void scheduleCmdEnd(EmuTime::param time);
701};
703
704} // namespace openmsx
705
706#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:34
Scheduler & getScheduler() const
Definition: MSXDevice.cc:137
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 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:16
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:34
void reset(EmuTime::param time) override
This method is called on reset.
Definition: V9990.cc:115
unsigned getCursorYOffset() const
In overscan mode the cursor position is still specified with 'normal' (non-overscan) y-coordinates.
Definition: V9990.hh:158
int getTopBorder() const
Definition: V9990.hh:337
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition: V9990.cc:109
void serialize(Archive &ar, unsigned version)
Definition: V9990.cc:875
bool isPalTiming() const
Is PAL timing active? This setting is fixed at start of frame.
Definition: V9990.hh:129
int getSpritePatternAddress(V9990DisplayMode m) const
Return the sprite pattern table base address.
Definition: V9990.hh:294
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:145
void cmdReady()
Command execution ready.
Definition: V9990.hh:288
byte getPaletteOffset() const
Get palette offset.
Definition: V9990.hh:100
byte getBackDropColor() const
Return the current back drop color.
Definition: V9990.hh:216
int getUCTicksThisFrame(EmuTime::param time) const
Get the number of elapsed UC ticks in this frame.
Definition: V9990.hh:121
V9990VRAM & getVRAM()
Obtain a reference to the V9990's VRAM.
Definition: V9990.hh:54
bool isInterlaced() const
Get interlace status.
Definition: V9990.hh:61
unsigned getLineWidth() const
Return the display width.
Definition: V9990.hh:272
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition: V9990.hh:68
unsigned getScrollBX() const
Returns the X scroll offset for screen B of P1 mode.
Definition: V9990.hh:234
~V9990() override
Definition: V9990.cc:95
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition: V9990.hh:194
unsigned getImageWidth() const
Return the image width.
Definition: V9990.hh:259
unsigned getPriorityControlX() const
Definition: V9990.hh:345
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition: V9990.hh:91
unsigned getColorDepth() const
Return the amount of bits per pixels.
Definition: V9990.hh:209
void setExternalVideoSource(bool enable)
Is there an external video source available to superimpose on.
Definition: V9990.hh:150
GetPaletteResult getPalette(int index) const
Definition: V9990.cc:672
byte getSpritePaletteOffset() const
return sprite palette offset
Definition: V9990.hh:307
static int UCtoX(int ticks, V9990DisplayMode mode)
Convert UC ticks to V9990 pixel position on a line.
Definition: V9990.hh:173
unsigned getPriorityControlY() const
Definition: V9990.hh:349
int getBottomBorder() const
Definition: V9990.hh:341
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:222
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:320
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:265
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
Definition: V9990.cc:100
bool isDisplayEnabled() const
Is the display enabled? Note this is simpler than the V99x8 version.
Definition: V9990.hh:84
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: V9990.cc:212
unsigned getScrollBY() const
Returns the Y scroll offset for screen B of P1 mode.
Definition: V9990.hh:240
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:228
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition: V9990.hh:246
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition: V9990.hh:333
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:789
V9990(const DeviceConfig &config)
Definition: V9990.cc:54
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition: V9990.hh:313
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:151
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:327
bool isOverScan() const
Returns true iff in overscan mode.
Definition: V9990.hh:135
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition: V9990.hh:75
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)
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:111
#define UNREACHABLE
Definition: unreachable.hh:38