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 {
137 return mode == one_of(B0, B2, B4);
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 case P1: x = ticks / 8; break;
179 case P2: x = ticks / 4; break;
180 case B0: x = ticks /12; break;
181 case B1: x = ticks / 8; break;
182 case B2: x = ticks / 6; break;
183 case B3: x = ticks / 4; break;
184 case B4: x = ticks / 3; break;
185 case B5: x = 1; break;
186 case B6: x = 1; break;
187 case B7: x = ticks / 2; break;
188 default: x = 1;
189 }
190 return x;
191 }
192
195 [[nodiscard]] inline V9990DisplayMode getDisplayMode() const {
196 return mode;
197 }
198
202
210 [[nodiscard]] inline unsigned getColorDepth() const {
211 return regs[SCREEN_MODE_0] & 0x03;
212 }
213
217 [[nodiscard]] inline byte getBackDropColor() const {
218 return regs[BACK_DROP_COLOR];
219 }
220
223 [[nodiscard]] inline unsigned getScrollAX() const {
224 return regs[SCROLL_CONTROL_AX0] + 8 * regs[SCROLL_CONTROL_AX1];
225 }
226
229 [[nodiscard]] inline unsigned getScrollAY() const {
230 return regs[SCROLL_CONTROL_AY0] + 256 * scrollAYHigh;
231 }
232
235 [[nodiscard]] inline unsigned getScrollBX() const {
236 return regs[SCROLL_CONTROL_BX0] + 8 * regs[SCROLL_CONTROL_BX1];
237 }
238
241 [[nodiscard]] inline unsigned getScrollBY() const {
242 return regs[SCROLL_CONTROL_BY0] + 256 * scrollBYHigh;
243 }
244
247 [[nodiscard]] inline unsigned getRollMask(unsigned maxMask) const {
248 static std::array<unsigned, 4> rollMasks = {
249 0xFFFF, // no rolling (use maxMask)
250 0x00FF,
251 0x01FF,
252 0x00FF // TODO check this (undocumented)
253 };
254 unsigned t = regs[SCROLL_CONTROL_AY1] >> 6;
255 return t ? rollMasks[t] : maxMask;
256 }
257
260 [[nodiscard]] inline unsigned getImageWidth() const {
261 switch (regs[SCREEN_MODE_0] & 0xC0) {
262 case 0x00: // P1
263 return 256;
264 case 0x40: // P2
265 return 512;
266 case 0x80: // Bx
267 default: // standby TODO check this
268 return (256 << ((regs[SCREEN_MODE_0] & 0x0C) >> 2));
269 }
270 }
273 [[nodiscard]] inline unsigned getLineWidth() const {
274 switch (getDisplayMode()) {
275 case B0: return 213;
276 case P1: case B1: return 320;
277 case B2: return 426;
278 case P2: case B3: return 640;
279 case B4: return 853;
280 case B5: case B6: return 1; // not supported
281 case B7: return 1280;
282 default:
283 UNREACHABLE; return 0;
284 }
285 }
286
289 inline void cmdReady() {
290 raiseIRQ(CMD_IRQ);
291 }
292
295 [[nodiscard]] inline int getSpritePatternAddress(V9990DisplayMode m) const {
296 switch (m) {
297 case P1:
298 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0E) << 14);
299 case P2:
300 return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0F) << 15);
301 default:
302 return 0;
303 }
304 }
305
308 [[nodiscard]] inline byte getSpritePaletteOffset() const {
309 return narrow_cast<byte>(regs[SPRITE_PALETTE_CONTROL] << 2);
310 }
311
314 [[nodiscard]] inline const V9990DisplayPeriod& getHorizontalTiming() const {
315 return *horTiming;
316 }
317
321 [[nodiscard]] inline int getLeftBorder() const {
322 return horTiming->blank + horTiming->border1 +
323 (((regs[DISPLAY_ADJUST] & 0x0F) ^ 7) - 8) * 8;
324 }
328 [[nodiscard]] inline int getRightBorder() const {
329 return getLeftBorder() + horTiming->display;
330 }
331
334 [[nodiscard]] inline const V9990DisplayPeriod& getVerticalTiming() const {
335 return *verTiming;
336 }
337
338 [[nodiscard]] inline int getTopBorder() const {
339 return verTiming->blank + verTiming->border1 +
340 (((regs[DISPLAY_ADJUST] >> 4) ^ 7) - 8);
341 }
342 [[nodiscard]] inline int getBottomBorder() const {
343 return getTopBorder() + verTiming->display;
344 }
345
346 [[nodiscard]] inline unsigned getPriorityControlX() const {
347 unsigned t = regs[PRIORITY_CONTROL] & 0x03;
348 return (t == 0) ? 256 : t << 6;
349 }
350 [[nodiscard]] inline unsigned getPriorityControlY() const {
351 unsigned t = regs[PRIORITY_CONTROL] & 0x0C;
352 return (t == 0) ? 256 : t << 4;
353 }
354
355 template<typename Archive>
356 void serialize(Archive& ar, unsigned version);
357
358private:
359 // VideoSystemChangeListener interface:
360 void preVideoSystemChange() noexcept override;
361 void postVideoSystemChange() noexcept override;
362
363 // Scheduler stuff
364 struct SyncBase : Schedulable {
365 explicit SyncBase(V9990& v9990) : Schedulable(v9990.getScheduler()) {}
368 protected:
369 ~SyncBase() = default;
370 };
371
372 struct SyncVSync final : SyncBase {
373 explicit SyncVSync(V9990& v9990) : SyncBase(v9990) {}
374 void executeUntil(EmuTime::param time) override {
375 auto& v9990 = OUTER(V9990, syncVSync);
376 v9990.execVSync(time);
377 }
378 } syncVSync;
379
380 struct SyncDisplayStart final : SyncBase {
381 explicit SyncDisplayStart(V9990& v9990) : SyncBase(v9990) {}
382 void executeUntil(EmuTime::param time) override {
383 auto& v9990 = OUTER(V9990, syncDisplayStart);
384 v9990.execDisplayStart(time);
385 }
386 } syncDisplayStart;
387
388 struct SyncVScan final : SyncBase {
389 explicit SyncVScan(V9990& v9990) : SyncBase(v9990) {}
390 void executeUntil(EmuTime::param time) override {
391 auto& v9990 = OUTER(V9990, syncVScan);
392 v9990.execVScan(time);
393 }
394 } syncVScan;
395
396 struct SyncHScan final : SyncBase {
397 explicit SyncHScan(V9990& v9990) : SyncBase(v9990) {}
398 void executeUntil(EmuTime::param /*time*/) override {
399 auto& v9990 = OUTER(V9990, syncHScan);
400 v9990.execHScan();
401 }
402 } syncHScan;
403
404 struct SyncSetMode final : SyncBase {
405 explicit SyncSetMode(V9990& v9990) : SyncBase(v9990) {}
406 void executeUntil(EmuTime::param time) override {
407 auto& v9990 = OUTER(V9990, syncSetMode);
408 v9990.execSetMode(time);
409 }
410 } syncSetMode;
411
412 struct SyncCmdEnd final : SyncBase {
413 explicit SyncCmdEnd(V9990& v9990) : SyncBase(v9990) {}
414 void executeUntil(EmuTime::param time) override {
415 auto& v9990 = OUTER(V9990, syncCmdEnd);
416 v9990.execCheckCmdEnd(time);
417 }
418 } syncCmdEnd;
419
420 void execVSync(EmuTime::param time);
421 void execDisplayStart(EmuTime::param time);
422 void execVScan(EmuTime::param time);
423 void execHScan();
424 void execSetMode(EmuTime::param time);
425 void execCheckCmdEnd(EmuTime::param time);
426
427 // --- types ------------------------------------------------------
428
431 enum IRQType : byte {
432 VER_IRQ = 1,
433 HOR_IRQ = 2,
434 CMD_IRQ = 4
435 };
436
439 enum PortId {
440 VRAM_DATA = 0,
441 PALETTE_DATA,
442 COMMAND_DATA,
443 REGISTER_DATA,
444 REGISTER_SELECT,
445 STATUS,
446 INTERRUPT_FLAG,
447 SYSTEM_CONTROL,
448 KANJI_ROM_0,
449 KANJI_ROM_1,
450 KANJI_ROM_2,
451 KANJI_ROM_3,
452 RESERVED_0,
453 RESERVED_1,
454 RESERVED_2,
455 RESERVED_3
456 };
457
460 enum RegisterId {
461 VRAM_WRITE_ADDRESS_0 = 0,
462 VRAM_WRITE_ADDRESS_1,
463 VRAM_WRITE_ADDRESS_2,
464 VRAM_READ_ADDRESS_0,
465 VRAM_READ_ADDRESS_1,
466 VRAM_READ_ADDRESS_2,
467 SCREEN_MODE_0,
468 SCREEN_MODE_1,
469 CONTROL,
470 INTERRUPT_0,
471 INTERRUPT_1,
472 INTERRUPT_2,
473 INTERRUPT_3,
474 PALETTE_CONTROL,
475 PALETTE_POINTER,
476 BACK_DROP_COLOR,
477 DISPLAY_ADJUST,
478 SCROLL_CONTROL_AY0,
479 SCROLL_CONTROL_AY1,
480 SCROLL_CONTROL_AX0,
481 SCROLL_CONTROL_AX1,
482 SCROLL_CONTROL_BY0,
483 SCROLL_CONTROL_BY1,
484 SCROLL_CONTROL_BX0,
485 SCROLL_CONTROL_BX1,
486 SPRITE_PATTERN_ADDRESS,
487 LCD_CONTROL,
488 PRIORITY_CONTROL,
489 SPRITE_PALETTE_CONTROL,
490 CMD_PARAM_SRC_ADDRESS_0 = 32,
491 CMD_PARAM_SRC_ADDRESS_1,
492 CMD_PARAM_SRC_ADDRESS_2,
493 CMD_PARAM_SRC_ADDRESS_3,
494 CMD_PARAM_DEST_ADDRESS_0,
495 CMD_PARAM_DEST_ADDRESS_1,
496 CMD_PARAM_DEST_ADDRESS_2,
497 CMD_PARAM_DEST_ADDRESS_3,
498 CMD_PARAM_SIZE_0,
499 CMD_PARAM_SIZE_1,
500 CMD_PARAM_SIZE_2,
501 CMD_PARAM_SIZE_3,
502 CMD_PARAM_ARGUMENT,
503 CMD_PARAM_LOGOP,
504 CMD_PARAM_WRITE_MASK_0,
505 CMD_PARAM_WRITE_MASK_1,
506 CMD_PARAM_FONT_COLOR_FC0,
507 CMD_PARAM_FONT_COLOR_FC1,
508 CMD_PARAM_FONT_COLOR_BC0,
509 CMD_PARAM_FONT_COLOR_BC1,
510 CMD_PARAM_OPCODE,
511 CMD_PARAM_BORDER_X_0,
512 CMD_PARAM_BORDER_X_1
513 };
514
515 // --- members ----------------------------------------------------
516
517 struct RegDebug final : SimpleDebuggable {
518 explicit RegDebug(V9990& v9990);
519 [[nodiscard]] byte read(unsigned address) override;
520 void write(unsigned address, byte value, EmuTime::param time) override;
521 } v9990RegDebug;
522
523 struct PalDebug final : SimpleDebuggable {
524 explicit PalDebug(V9990& v9990);
525 [[nodiscard]] byte read(unsigned address) override;
526 void write(unsigned address, byte value, EmuTime::param time) override;
527 } v9990PalDebug;
528
529 IRQHelper irq;
530
531 Display& display;
532
533 TclCallback invalidRegisterReadCallback;
534 TclCallback invalidRegisterWriteCallback;
535
538 V9990VRAM vram;
539 unsigned vramReadPtr, vramWritePtr;
540 byte vramReadBuffer;
541
544 V9990CmdEngine cmdEngine;
545
548 std::unique_ptr<V9990Renderer> renderer;
549
552 Clock<V9990DisplayTiming::UC_TICKS_PER_SECOND> frameStartTime;
553
556 EmuTime hScanSyncTime;
557
560 const V9990DisplayPeriod* horTiming;
561 const V9990DisplayPeriod* verTiming;
562
565 V9990DisplayMode mode;
566
569 std::array<byte, 0x100> palette;
570
573 byte status;
574
577 byte pendingIRQs = 0;
578
581 std::array<byte, 0x40> regs = {}; // fill with zero
582 byte regSelect;
583
586 bool palTiming{false};
587
591 bool interlaced{false};
592
595 bool isDisplayArea{false};
596
602 bool displayEnabled{false};
603
612 byte scrollAYHigh;
613 byte scrollBYHigh;
614
619 bool systemReset;
620
625 bool externalVideoSource{false};
626
630 bool superimposing{false};
631
632 // --- methods ----------------------------------------------------
633
634 void setHorizontalTiming();
635 void setVerticalTiming();
636
637 [[nodiscard]] V9990ColorMode getColorMode(byte pal_ctrl) const;
638
643 [[nodiscard]] inline unsigned getVRAMAddr(RegisterId base) const;
644
649 inline void setVRAMAddr(RegisterId base, unsigned addr);
650
656 [[nodiscard]] byte readRegister(byte reg, EmuTime::param time) const;
657
663 void writeRegister(byte reg, byte val, EmuTime::param time);
664
670 void writePaletteRegister(byte reg, byte val, EmuTime::param time);
671
674 void syncAtNextLine(SyncBase& type, EmuTime::param time);
675
679 void createRenderer(EmuTime::param time);
680
684 void frameStart(EmuTime::param time);
685
689 void raiseIRQ(IRQType irqType);
690
693 [[nodiscard]] V9990DisplayMode calcDisplayMode() const;
694 void setDisplayMode(V9990DisplayMode newMode);
695
700 void scheduleHscan(EmuTime::param time);
701
704 void scheduleCmdEnd(EmuTime::param time);
705};
707
708} // namespace openmsx
709
710#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:36
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: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:338
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:888
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:295
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:289
byte getPaletteOffset() const
Get palette offset.
Definition: V9990.hh:101
byte getBackDropColor() const
Return the current back drop color.
Definition: V9990.hh:217
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:273
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:235
~V9990() override
Definition: V9990.cc:105
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition: V9990.hh:195
unsigned getImageWidth() const
Return the image width.
Definition: V9990.hh:260
unsigned getPriorityControlX() const
Definition: V9990.hh:346
bool spritesEnabled() const
Are sprites (cursors) enabled?
Definition: V9990.hh:92
unsigned getColorDepth() const
Return the amount of bits per pixels.
Definition: V9990.hh:210
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:308
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:350
int getBottomBorder() const
Definition: V9990.hh:342
unsigned getScrollAX() const
Returns the X scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:223
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:321
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:241
unsigned getScrollAY() const
Returns the Y scroll offset for screen A of P1 and other modes.
Definition: V9990.hh:229
unsigned getRollMask(unsigned maxMask) const
Returns the vertical roll mask.
Definition: V9990.hh:247
const V9990DisplayPeriod & getVerticalTiming() const
Get vertical display timings.
Definition: V9990.hh:334
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:802
V9990(const DeviceConfig &config)
Definition: V9990.cc:54
const V9990DisplayPeriod & getHorizontalTiming() const
Get horizontal display timings.
Definition: V9990.hh:314
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:328
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: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:112
#define UNREACHABLE
Definition: unreachable.hh:38