OXYGENHUB
◆ main openmsx / src / video / v9990 / V9990.hh
18.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef V9990_HH
2
#define V9990_HH
3
4
#include "V9990CmdEngine.hh"
5
#include "V9990DisplayTiming.hh"
6
#include "V9990ModeEnum.hh"
7
#include "V9990VRAM.hh"
8
9
#include "Clock.hh"
10
#include "IRQHelper.hh"
11
#include "MSXDevice.hh"
12
#include "Schedulable.hh"
13
#include "SimpleDebuggable.hh"
14
#include "TclCallback.hh"
15
#include "VideoSystemChangeListener.hh"
16
17
#include "narrow.hh"
18
#include "one_of.hh"
19
#include "outer.hh"
20
#include "serialize_meta.hh"
21
#include "unreachable.hh"
22
23
#include <array>
24
#include <cstdint>
25
#include <memory>
26
27
namespace openmsx {
28
29
class PostProcessor;
30
class Display;
31
class V9990Renderer;
32
33
/** Implementation of the Yamaha V9990 VDP as used in the GFX9000
34
  * cartridge by Sunrise.
35
  */
36
class V9990 final : public MSXDevice, private VideoSystemChangeListener
37
{
38
public:
39
	explicit V9990(const DeviceConfig& config);
40
	~V9990() override;
41
42
	// MSXDevice interface:
43
	void powerUp(EmuTime time) override;
44
	void reset(EmuTime time) override;
45
	[[nodiscard]] uint8_t readIO(uint16_t port, EmuTime time) override;
46
	[[nodiscard]] uint8_t peekIO(uint16_t port, EmuTime time) const override;
47
	void writeIO(uint16_t port, uint8_t value, EmuTime time) override;
48
49
	/** Used by Video9000 to be able to couple the VDP and V9990 output.
50
	 * Can return nullptr in case of renderer=none. This value can change
51
	 * over the lifetime of the V9990 object (on renderer switch).
52
	 */
53
	[[nodiscard]] PostProcessor* getPostProcessor() const;
54
55
	/** Obtain a reference to the V9990's VRAM
56
	  */
57
	[[nodiscard]] V9990VRAM& getVRAM() {
58
		return vram;
59
	}
60
61
	/** Get interlace status.
62
	  * @return True iff interlace is enabled.
63
	  */
64
	[[nodiscard]] bool isInterlaced() const {
65
		return interlaced;
66
	}
67
68
	/** Get even/odd page alternation status.
69
	  * @return True iff even/odd page alternation is enabled.
70
	  */
71
	[[nodiscard]] bool isEvenOddEnabled() const {
72
		return (regs[SCREEN_MODE_1] & 0x04) != 0;
73
	}
74
75
	/** Is the even or odd field being displayed?
76
	  * @return True iff the odd lines should be displayed.
77
	  */
78
	[[nodiscard]] bool getEvenOdd() const {
79
		return (status & 0x02) != 0;
80
	}
81
82
	/** Is the display enabled?
83
	  *  Note this is simpler than the V99x8 version. Probably ok
84
	  *  because V9990 doesn't have the same overscan trick (?)
85
	  * @return true iff enabled
86
	  */
87
	[[nodiscard]] bool isDisplayEnabled() const {
88
		return isDisplayArea && displayEnabled;
89
	}
90
91
	/** Are sprites (cursors) enabled?
92
	  * @return true iff enabled
93
	  */
94
	[[nodiscard]] bool spritesEnabled() const {
95
		return !(regs[CONTROL] & 0x40);
96
	}
97
98
	/** Get palette offset.
99
	  * Result is between [0..15] (this represents a logical number
100
	  * between [0..63] with lowest two bits always 0).
101
	  * @return palette offset
102
	  */
103
	[[nodiscard]] uint8_t getPaletteOffset() const {
104
		return (regs[PALETTE_CONTROL] & 0x0F);
105
	}
106
107
	/** Get palette entry
108
	  * @param[in] index The palette index
109
	  * ys is only true iff
110
	  *  - bit 5 (YSE) in R#8 is set
111
	  *  - there is an external video source set
112
	  *  - the YS bit in the palette-entry is set
113
	  */
114
	struct GetPaletteResult {
115
		uint8_t r, g, b;
116
		bool ys;
117
	};
118
	[[nodiscard]] GetPaletteResult getPalette(int index) const;
119
120
	/** Get the number of elapsed UC ticks in this frame.
121
	  * @param  time Point in emulated time.
122
	  * @return      Number of UC ticks.
123
	  */
124
	[[nodiscard]] int getUCTicksThisFrame(EmuTime time) const {
125
		return narrow<int>(frameStartTime.getTicksTill(time));
126
	}
127
128
	/** Is PAL timing active?
129
	  * This setting is fixed at start of frame.
130
	  * @return True if PAL timing, false if NTSC timing.
131
	  */
132
	[[nodiscard]] bool isPalTiming() const {
133
		return palTiming;
134
	}
135
136
	/** Returns true iff in overscan mode
137
	  */
138
	[[nodiscard]] bool isOverScan() const {
139
		return mode == one_of(V9990DisplayMode::B0, V9990DisplayMode::B2, V9990DisplayMode::B4);
140
	}
141
142
	/** Should this frame be superimposed?
143
	  * This is a combination of bit 5 (YSE) in R#8 and the presence of an
144
	  * external video source (see setExternalVideoSource()). Though
145
	  * because this property only changes once per frame we can't directly
146
	  * calculate it like that.
147
	  */
148
	[[nodiscard]] bool isSuperimposing() const {
149
		return superimposing;
150
	}
151
152
	/** Is there an external video source available to superimpose on. */
153
	void setExternalVideoSource(bool enable) {
154
		externalVideoSource = enable;
155
	}
156
157
	/** In overscan mode the cursor position is still specified with
158
	  * 'normal' (non-overscan) y-coordinates. This method returns the
159
	  * offset between those two coord-systems.
160
	  */
161
	[[nodiscard]] unsigned getCursorYOffset() const {
162
		// TODO vertical set-adjust may or may not influence this,
163
		//      need to investigate that.
164
		if (!isOverScan()) return 0;
165
		return isPalTiming()
166
			? V9990DisplayTiming::displayPAL_MCLK .border1
167
			: V9990DisplayTiming::displayNTSC_MCLK.border1;
168
	}
169
170
	/** Convert UC ticks to V9990 pixel position on a line
171
	  * @param ticks  Nr of UC Ticks
172
	  * @param mode   Display mode
173
	  * @return       Pixel position
174
	  * TODO: Move this to V9990DisplayTiming??
175
	  */
176
	[[nodiscard]] static int UCtoX(int ticks, V9990DisplayMode mode) {
177
		int x;
178
		ticks = ticks % V9990DisplayTiming::UC_TICKS_PER_LINE;
179
		switch (mode) {
180
		using enum V9990DisplayMode;
181
		case P1: x = ticks / 8;  break;
182
		case P2: x = ticks / 4;  break;
183
		case B0: x = ticks /12;  break;
184
		case B1: x = ticks / 8;  break;
185
		case B2: x = ticks / 6;  break;
186
		case B3: x = ticks / 4;  break;
187
		case B4: x = ticks / 3;  break;
188
		case B5: x = 1;          break;
189
		case B6: x = 1;          break;
190
		case B7: x = ticks / 2;  break;
191
		default: x = 1;
192
		}
193
		return x;
194
	}
195
196
	/** Return the current display mode
197
	  */
198
	[[nodiscard]] V9990DisplayMode getDisplayMode() const {
199
		return mode;
200
	}
201
202
	/** Return the current color mode
203
	  */
204
	V9990ColorMode getColorMode() const;
205
206
	/** Return the amount of bits per pixels.
207
	  * Result is only meaningful for Bx modes.
208
	  * @return Bpp 0 ->  2bpp
209
	  *             1 ->  4bpp
210
	  *             2 ->  8bpp
211
	  *             3 -> 16bpp
212
	  */
213
	[[nodiscard]] unsigned getColorDepth() const {
214
		return regs[SCREEN_MODE_0] & 0x03;
215
	}
216
217
	/** Return the current back drop color
218
	  * @return  Index the color palette
219
	  */
220
	[[nodiscard]] uint8_t getBackDropColor() const {
221
		return regs[BACK_DROP_COLOR];
222
	}
223
224
	/** Returns the X scroll offset for screen A of P1 and other modes
225
	  */
226
	[[nodiscard]] unsigned getScrollAX() const {
227
		return regs[SCROLL_CONTROL_AX0] + 8 * regs[SCROLL_CONTROL_AX1];
228
	}
229
230
	/** Returns the Y scroll offset for screen A of P1 and other modes
231
	  */
232
	[[nodiscard]] unsigned getScrollAY() const {
233
		return regs[SCROLL_CONTROL_AY0] + 256 * scrollAYHigh;
234
	}
235
236
	/** Returns the X scroll offset for screen B of P1 mode
237
	  */
238
	[[nodiscard]] unsigned getScrollBX() const {
239
		return regs[SCROLL_CONTROL_BX0] + 8 * regs[SCROLL_CONTROL_BX1];
240
	}
241
242
	/** Returns the Y scroll offset for screen B of P1 mode
243
	  */
244
	[[nodiscard]] unsigned getScrollBY() const {
245
		return regs[SCROLL_CONTROL_BY0] + 256 * scrollBYHigh;
246
	}
247
248
	/** Returns the vertical roll mask
249
	  */
250
	[[nodiscard]] unsigned getRollMask(unsigned maxMask) const {
251
		static std::array<unsigned, 4> rollMasks = {
252
			0xFFFF, // no rolling (use maxMask)
253
			0x00FF,
254
			0x01FF,
255
			0x00FF // TODO check this (undocumented)
256
		};
257
		unsigned t = regs[SCROLL_CONTROL_AY1] >> 6;
258
		return t ? rollMasks[t] : maxMask;
259
	}
260
261
	/** Return the image width
262
	  */
263
	[[nodiscard]] unsigned getImageWidth() const {
264
		switch (regs[SCREEN_MODE_0] & 0xC0) {
265
		case 0x00: // P1
266
			return 256;
267
		case 0x40: // P2
268
			return 512;
269
		case 0x80: // Bx
270
		default:   // standby TODO check this
271
			return (256 << ((regs[SCREEN_MODE_0] & 0x0C) >> 2));
272
		}
273
	}
274
	/** Return the display width
275
	  */
276
	[[nodiscard]] unsigned getLineWidth() const {
277
		switch (getDisplayMode()) {
278
		using enum V9990DisplayMode;
279
		case B0:          return  213;
280
		case P1: case B1: return  320;
281
		case B2:          return  426;
282
		case P2: case B3: return  640;
283
		case B4:          return  853;
284
		case B5: case B6: return    1; // not supported
285
		case B7:          return 1280;
286
		default:
287
			UNREACHABLE;
288
		}
289
	}
290
291
	/** Command execution ready
292
	  */
293
	void cmdReady() {
294
		raiseIRQ(CMD_IRQ);
295
	}
296
297
	/** Return the sprite pattern table base address
298
	  */
299
	[[nodiscard]] int getSpritePatternAddress(V9990DisplayMode m) const {
300
		switch (m) {
301
		case V9990DisplayMode::P1:
302
			return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0E) << 14);
303
		case V9990DisplayMode::P2:
304
			return (int(regs[SPRITE_PATTERN_ADDRESS] & 0x0F) << 15);
305
		default:
306
			return 0;
307
		}
308
	}
309
310
	/** return sprite palette offset
311
	  */
312
	[[nodiscard]] uint8_t getSpritePaletteOffset() const {
313
		return narrow_cast<uint8_t>(regs[SPRITE_PALETTE_CONTROL] << 2);
314
	}
315
316
	/** Get horizontal display timings
317
	 */
318
	[[nodiscard]] const V9990DisplayPeriod& getHorizontalTiming() const {
319
		return *horTiming;
320
	}
321
322
	/** Get the number of VDP clock-ticks between the start of the line and
323
	  * the end of the left border.
324
	  */
325
	[[nodiscard]] int getLeftBorder() const {
326
		return horTiming->blank + horTiming->border1 +
327
		       (((regs[DISPLAY_ADJUST] & 0x0F) ^ 7) - 8) * 8;
328
	}
329
	/** Get the number of VDP clock-ticks between the start of the line and
330
	  * the end of the right border.
331
	  */
332
	[[nodiscard]] int getRightBorder() const {
333
		return getLeftBorder() + horTiming->display;
334
	}
335
336
	/** Get vertical display timings
337
	 */
338
	[[nodiscard]] const V9990DisplayPeriod& getVerticalTiming() const {
339
		return *verTiming;
340
	}
341
342
	[[nodiscard]] int getTopBorder() const {
343
		return verTiming->blank + verTiming->border1 +
344
		       (((regs[DISPLAY_ADJUST] >> 4) ^ 7) - 8);
345
	}
346
	[[nodiscard]] int getBottomBorder() const {
347
		return getTopBorder() + verTiming->display;
348
	}
349
350
	[[nodiscard]] unsigned getPriorityControlX() const {
351
		unsigned t = regs[PRIORITY_CONTROL] & 0x03;
352
		return (t == 0) ? 256 : t << 6;
353
	}
354
	[[nodiscard]] unsigned getPriorityControlY() const {
355
		unsigned t = regs[PRIORITY_CONTROL] & 0x0C;
356
		return (t == 0) ? 256 : t << 4;
357
	}
358
359
	template<typename Archive>
360
	void serialize(Archive& ar, unsigned version);
361
362
private:
363
	// VideoSystemChangeListener interface:
364
	void preVideoSystemChange() noexcept override;
365
	void postVideoSystemChange() noexcept override;
366
367
	// Scheduler stuff
368
	struct SyncBase : Schedulable {
369
		explicit SyncBase(const V9990& v9990) : Schedulable(v9990.getScheduler()) {}
370
		using Schedulable::setSyncPoint;
371
		using Schedulable::removeSyncPoint;
372
	protected:
373
		~SyncBase() = default;
374
	};
375
376
	struct SyncVSync final : SyncBase {
377
		using SyncBase::SyncBase;
378
		void executeUntil(EmuTime time) override {
379
			auto& v9990 = OUTER(V9990, syncVSync);
380
			v9990.execVSync(time);
381
		}
382
	} syncVSync;
383
384
	struct SyncDisplayStart final : SyncBase {
385
		using SyncBase::SyncBase;
386
		void executeUntil(EmuTime time) override {
387
			auto& v9990 = OUTER(V9990, syncDisplayStart);
388
			v9990.execDisplayStart(time);
389
		}
390
	} syncDisplayStart;
391
392
	struct SyncVScan final : SyncBase {
393
		using SyncBase::SyncBase;
394
		void executeUntil(EmuTime time) override {
395
			auto& v9990 = OUTER(V9990, syncVScan);
396
			v9990.execVScan(time);
397
		}
398
	} syncVScan;
399
400
	struct SyncHScan final : SyncBase {
401
		using SyncBase::SyncBase;
402
		void executeUntil(EmuTime /*time*/) override {
403
			auto& v9990 = OUTER(V9990, syncHScan);
404
			v9990.execHScan();
405
		}
406
	} syncHScan;
407
408
	struct SyncSetMode final : SyncBase {
409
		using SyncBase::SyncBase;
410
		void executeUntil(EmuTime time) override {
411
			auto& v9990 = OUTER(V9990, syncSetMode);
412
			v9990.execSetMode(time);
413
		}
414
	} syncSetMode;
415
416
	struct SyncCmdEnd final : SyncBase {
417
		using SyncBase::SyncBase;
418
		void executeUntil(EmuTime time) override {
419
			auto& v9990 = OUTER(V9990, syncCmdEnd);
420
			v9990.execCheckCmdEnd(time);
421
		}
422
	} syncCmdEnd;
423
424
	void execVSync(EmuTime time);
425
	void execDisplayStart(EmuTime time);
426
	void execVScan(EmuTime time);
427
	void execHScan();
428
	void execSetMode(EmuTime time);
429
	void execCheckCmdEnd(EmuTime time);
430
431
	// --- types ------------------------------------------------------
432
433
	/** IRQ types
434
	  */
435
	enum IRQType : uint8_t {
436
		VER_IRQ = 1,
437
		HOR_IRQ = 2,
438
		CMD_IRQ = 4
439
	};
440
441
	/** I/O Ports
442
	  */
443
	enum PortId : uint8_t {
444
		VRAM_DATA = 0,
445
		PALETTE_DATA,
446
		COMMAND_DATA,
447
		REGISTER_DATA,
448
		REGISTER_SELECT,
449
		STATUS,
450
		INTERRUPT_FLAG,
451
		SYSTEM_CONTROL,
452
		KANJI_ROM_0,
453
		KANJI_ROM_1,
454
		KANJI_ROM_2,
455
		KANJI_ROM_3,
456
		RESERVED_0,
457
		RESERVED_1,
458
		RESERVED_2,
459
		RESERVED_3
460
	};
461
462
	/** Registers
463
	  */
464
	enum RegisterId : uint8_t {
465
		VRAM_WRITE_ADDRESS_0 = 0,
466
		VRAM_WRITE_ADDRESS_1,
467
		VRAM_WRITE_ADDRESS_2,
468
		VRAM_READ_ADDRESS_0,
469
		VRAM_READ_ADDRESS_1,
470
		VRAM_READ_ADDRESS_2,
471
		SCREEN_MODE_0,
472
		SCREEN_MODE_1,
473
		CONTROL,
474
		INTERRUPT_0,
475
		INTERRUPT_1,
476
		INTERRUPT_2,
477
		INTERRUPT_3,
478
		PALETTE_CONTROL,
479
		PALETTE_POINTER,
480
		BACK_DROP_COLOR,
481
		DISPLAY_ADJUST,
482
		SCROLL_CONTROL_AY0,
483
		SCROLL_CONTROL_AY1,
484
		SCROLL_CONTROL_AX0,
485
		SCROLL_CONTROL_AX1,
486
		SCROLL_CONTROL_BY0,
487
		SCROLL_CONTROL_BY1,
488
		SCROLL_CONTROL_BX0,
489
		SCROLL_CONTROL_BX1,
490
		SPRITE_PATTERN_ADDRESS,
491
		LCD_CONTROL,
492
		PRIORITY_CONTROL,
493
		SPRITE_PALETTE_CONTROL,
494
		CMD_PARAM_SRC_ADDRESS_0 = 32,
495
		CMD_PARAM_SRC_ADDRESS_1,
496
		CMD_PARAM_SRC_ADDRESS_2,
497
		CMD_PARAM_SRC_ADDRESS_3,
498
		CMD_PARAM_DEST_ADDRESS_0,
499
		CMD_PARAM_DEST_ADDRESS_1,
500
		CMD_PARAM_DEST_ADDRESS_2,
501
		CMD_PARAM_DEST_ADDRESS_3,
502
		CMD_PARAM_SIZE_0,
503
		CMD_PARAM_SIZE_1,
504
		CMD_PARAM_SIZE_2,
505
		CMD_PARAM_SIZE_3,
506
		CMD_PARAM_ARGUMENT,
507
		CMD_PARAM_LOGOP,
508
		CMD_PARAM_WRITE_MASK_0,
509
		CMD_PARAM_WRITE_MASK_1,
510
		CMD_PARAM_FONT_COLOR_FC0,
511
		CMD_PARAM_FONT_COLOR_FC1,
512
		CMD_PARAM_FONT_COLOR_BC0,
513
		CMD_PARAM_FONT_COLOR_BC1,
514
		CMD_PARAM_OPCODE,
515
		CMD_PARAM_BORDER_X_0,
516
		CMD_PARAM_BORDER_X_1
517
	};
518
519
	// --- members ----------------------------------------------------
520
521
	struct RegDebug final : SimpleDebuggable {
522
		explicit RegDebug(const V9990& v9990);
523
		[[nodiscard]] uint8_t read(unsigned address) override;
524
		void write(unsigned address, uint8_t value, EmuTime time) override;
525
		void readBlock(unsigned start, std::span<uint8_t> output) override;
526
	} v9990RegDebug;
527
528
	struct PalDebug final : SimpleDebuggable {
529
		explicit PalDebug(const V9990& v9990);
530
		[[nodiscard]] uint8_t read(unsigned address) override;
531
		void write(unsigned address, uint8_t value, EmuTime time) override;
532
		void readBlock(unsigned start, std::span<uint8_t> output) override;
533
	} v9990PalDebug;
534
535
	IRQHelper irq;
536
537
	Display& display;
538
539
	TclCallback   invalidRegisterReadCallback;
540
	TclCallback   invalidRegisterWriteCallback;
541
542
	/** VRAM
543
	  */
544
	V9990VRAM vram;
545
	unsigned vramReadPtr, vramWritePtr;
546
	uint8_t vramReadBuffer;
547
548
	/** Command Engine
549
	  */
550
	V9990CmdEngine cmdEngine;
551
552
	/** Renderer
553
	  */
554
	std::unique_ptr<V9990Renderer> renderer;
555
556
	/** Emulation time when this frame was started (VSYNC)
557
	  */
558
	Clock<V9990DisplayTiming::UC_TICKS_PER_SECOND> frameStartTime;
559
560
	/** Time of the last set HSCAN sync point
561
	  */
562
	EmuTime hScanSyncTime;
563
564
	/** Display timings
565
	  */
566
	const V9990DisplayPeriod* horTiming;
567
	const V9990DisplayPeriod* verTiming;
568
569
	/** Store display mode because it's queried a lot
570
	  */
571
	V9990DisplayMode mode;
572
573
	/** Palette
574
	  */
575
	std::array<uint8_t, 0x100> palette;
576
577
	/** Status port (P#5)
578
	  */
579
	uint8_t status;
580
581
	/** Interrupt flag (P#6)
582
	  */
583
	uint8_t pendingIRQs = 0;
584
585
	/** Registers
586
	  */
587
	std::array<uint8_t, 0x40> regs = {}; // fill with zero
588
	uint8_t regSelect;
589
590
	/** Is PAL timing active?  False means NTSC timing
591
	  */
592
	bool palTiming{false};
593
594
	/** Is interlace active?
595
	  * @see isInterlaced.
596
	  */
597
	bool interlaced{false};
598
599
	/** Is the current scan position inside the display area?
600
	  */
601
	bool isDisplayArea{false};
602
603
	/** Is display enabled. Note that this is not always the same as bit 7
604
	  * of the CONTROL register because the display enable status change
605
	  * only takes place at the start of the next frame.
606
	  * Note: on V99x8, display enable takes effect the next line
607
	  */
608
	bool displayEnabled{false};
609
610
	/** Changing high byte of vertical scroll registers only takes effect
611
	  * at the start of the next page. These members hold the current
612
	  * value of the scroll value.
613
	  * note: writing the low byte has effect immediately (or at the next
614
	  *       line, TODO investigate this). But the effect is not the same
615
	  *       as on V99x8, see V9990PixelRenderer::updateScrollAYLow() for
616
	  *       details.
617
	  */
618
	uint8_t scrollAYHigh;
619
	uint8_t scrollBYHigh;
620
621
	/** Corresponds to bit 1 in the System Control Port.
622
	  * When this is true, all registers are held in the 'power ON reset'
623
	  * state, so writes to registers are ignored.
624
	  */
625
	bool systemReset;
626
627
	/** Is there an external video source available to superimpose on?
628
	  * In 32bpp we could just always output an alpha channel. But in
629
	  * 16bpp we only want to output the special color-key when later
630
	  * it will be replaced by a pixel from the external video source. */
631
	bool externalVideoSource{false};
632
633
	/** Combination of 'externalVideoSource' and R#8 bit 5 (YSE). This
634
	  * variable only changes once per frame, so we can't directly
635
	  * calculate it from the two former states. */
636
	bool superimposing{false};
637
638
	// --- methods ----------------------------------------------------
639
640
	void setHorizontalTiming();
641
	void setVerticalTiming();
642
643
	[[nodiscard]] V9990ColorMode getColorMode(uint8_t pal_ctrl) const;
644
645
	/** Get VRAM read or write address from V9990 registers
646
	  * @param base  VRAM_READ_ADDRESS_0 or VRAM_WRITE_ADDRESS_0
647
	  * @returns     VRAM read or write address
648
	  */
649
	[[nodiscard]] unsigned getVRAMAddr(RegisterId base) const;
650
651
	/** set VRAM read or write address into V9990 registers
652
	  * @param base  VRAM_READ_ADDRESS_0 or VRAM_WRITE_ADDRESS_0
653
	  * @param addr  Address to set
654
	  */
655
	void setVRAMAddr(RegisterId base, unsigned addr);
656
657
	/** Read V9990 register value
658
	  * @param reg   Register to read from
659
	  * @param time  Moment in emulated time to read register
660
	  * @returns     Register value
661
	  */
662
	[[nodiscard]] uint8_t readRegister(uint8_t reg, EmuTime time) const;
663
664
	/** Write V9990 register value
665
	  * @param reg   Register to write to
666
	  * @param val   Value to write
667
	  * @param time  Moment in emulated time to write register
668
	  */
669
	void writeRegister(uint8_t reg, uint8_t val, EmuTime time);
670
671
	/** Write V9990 palette register
672
	  * @param reg   Register to write to
673
	  * @param val   Value to write
674
	  * @param time  Moment in emulated time to write register
675
	  */
676
	void writePaletteRegister(uint8_t reg, uint8_t val, EmuTime time);
677
678
	/** Schedule a sync point at the start of the next line
679
	 */
680
	void syncAtNextLine(SyncBase& type, EmuTime time) const;
681
682
	/** Create a new renderer.
683
	  * @param time  Moment in emulated time to create the renderer
684
	  */
685
	void createRenderer(EmuTime time);
686
687
	/** Start a new frame.
688
	  * @param time  Moment in emulated time to start the frame
689
	  */
690
	void frameStart(EmuTime time);
691
692
	/** Raise an IRQ
693
	  * @param irqType  Type of IRQ
694
	  */
695
	void raiseIRQ(IRQType irqType);
696
697
	/** Precalculate the display mode
698
	  */
699
	[[nodiscard]] V9990DisplayMode calcDisplayMode() const;
700
	void setDisplayMode(V9990DisplayMode newMode);
701
702
	/** Calculate the moment in time the next line interrupt will occur
703
	  * @param time The current time
704
	  * @result Timestamp for next hor irq
705
	  */
706
	void scheduleHscan(EmuTime time);
707
708
	/** Estimate when the current (if any) command will finish.
709
	 */
710
	void scheduleCmdEnd(EmuTime time);
711
};
712
SERIALIZE_CLASS_VERSION(V9990, 5);
713
714
} // namespace openmsx
715
716
#endif