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
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: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
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
IntHelper< IRQSource > IRQHelper
Definition IRQHelper.hh:122
#define OUTER(type, member)
Definition outer.hh:41
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION)
A period, either horizontal or vertical, starts with a synchronisation pulse followed by a blank peri...
#define UNREACHABLE