OXYGENHUB
◆ main openmsx / src / video / VDP.hh
49.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef VDP_HH
2
#define VDP_HH
3
4
#include "DisplayMode.hh"
5
#include "VideoSystemChangeListener.hh"
6
#include "gl_vec.hh"
7
8
#include "Clock.hh"
9
#include "EnumSetting.hh"
10
#include "IRQHelper.hh"
11
#include "InfoTopic.hh"
12
#include "MSXDevice.hh"
13
#include "Schedulable.hh"
14
#include "SimpleDebuggable.hh"
15
#include "TclCallback.hh"
16
17
#include "Observer.hh"
18
#include "narrow.hh"
19
#include "outer.hh"
20
21
#include <array>
22
#include <cstdint>
23
#include <memory>
24
25
namespace openmsx {
26
27
class PostProcessor;
28
class Renderer;
29
class VDPCmdEngine;
30
class VDPVRAM;
31
class MSXCPU;
32
class SpriteChecker;
33
class Display;
34
class RawFrame;
35
class Setting;
36
namespace VDPAccessSlots {
37
	enum class Delta : int;
38
	class Calculator;
39
}
40
41
/** Unified implementation of MSX Video Display Processors (VDPs).
42
  * MSX1 VDP is Texas Instruments TMS9918A or TMS9928A.
43
  * MSX2 VDP is Yamaha V9938.
44
  * MSX2+ and turbo R VDP is Yamaha V9958.
45
  *
46
  * Current implementation is written by Maarten ter Huurne,
47
  * with some contributions from other openMSX developers.
48
  * The first implementation was based on Sean Young's code from MESS,
49
  * which was in turn based on code by Mike Balfour.
50
  * Integration of MESS code into openMSX was done by David Heremans.
51
  *
52
  * All undocumented features as described in the following file
53
  * should be emulated:
54
  * http://www.msxnet.org/tech/tms9918a.txt
55
  *
56
  * This class is the VDP core, it uses a separate Renderer to
57
  * convert the VRAM contents into pixels on the host machine.
58
  * Communicates with the Renderer through a pull variant of the
59
  * Observer pattern: a VDP object fires events when its state changes,
60
  * the Renderer can retrieve the new state if necessary by calling
61
  * methods on the VDP object.
62
  *
63
  * A note about timing: the start of a frame or line is defined as
64
  * the starting time of the corresponding sync (vsync, hsync).
65
  */
66
class VDP final : public MSXDevice, private VideoSystemChangeListener
67
                , private Observer<Setting>
68
{
69
public:
70
	/** Number of VDP clock ticks per second.
71
	  */
72
	static constexpr int TICKS_PER_SECOND = 3579545 * 6; // 21.5MHz;
73
	using VDPClock = Clock<TICKS_PER_SECOND>;
74
75
	/** Number of VDP clock ticks per line.
76
	  */
77
	static constexpr int TICKS_PER_LINE = 1368;
78
79
	// Number of lines per frame.
80
	static constexpr int PAL_LINES = 313;
81
	static constexpr int NTSC_LINES = 262;
82
	static constexpr int NUM_LINES_MAX = std::max(PAL_LINES, NTSC_LINES);
83
84
	explicit VDP(const DeviceConfig& config);
85
	~VDP() override;
86
87
	void powerUp(EmuTime time) override;
88
	void reset(EmuTime time) override;
89
	[[nodiscard]] uint8_t readIO(uint16_t port, EmuTime time) override;
90
	[[nodiscard]] uint8_t peekIO(uint16_t port, EmuTime time) const override;
91
	void writeIO(uint16_t port, uint8_t value, EmuTime time) override;
92
93
	void getExtraDeviceInfo(TclObject& result) const override;
94
	[[nodiscard]] std::string_view getVersionString() const;
95
96
	[[nodiscard]] uint8_t peekRegister(unsigned address, EmuTime time) const;
97
	[[nodiscard]] uint8_t peekStatusReg(uint8_t reg, EmuTime time) const;
98
99
	/** VDP control register has changed, work out the consequences.
100
	  */
101
	void changeRegister(uint8_t reg, uint8_t val, EmuTime time);
102
103
104
	/** Used by Video9000 to be able to couple the VDP and V9990 output.
105
	 * Can return nullptr in case of renderer=none. This value can change
106
	 * over the lifetime of the VDP object (on renderer switch).
107
	 */
108
	[[nodiscard]] PostProcessor* getPostProcessor() const;
109
110
	/** Is this an MSX1 VDP?
111
	  * @return True if this is an MSX1 VDP
112
	  *   False otherwise.
113
	  */
114
	[[nodiscard]] bool isMSX1VDP() const {
115
		return (version & VM_MSX1) != 0;
116
	}
117
118
	/** Is this a VDP only capable of PAL?
119
	  * @return True iff this is a PAL only VDP
120
	  */
121
	[[nodiscard]] bool isVDPwithPALonly() const {
122
		return (version & VM_PAL) != 0;
123
	}
124
125
	/** Is this a VDP that lacks mirroring?
126
	  * @return True iff this VDP lacks the screen 2 mirrored mode
127
	  */
128
	[[nodiscard]] bool vdpLacksMirroring() const {
129
		return (version & VM_NO_MIRRORING) != 0;
130
	}
131
132
	/** Is this a VDP that has pattern/color table mirroring?
133
	 * @return True iff this VDP has pattern/color table mirroring
134
	 */
135
	[[nodiscard]] bool vdpHasPatColMirroring() const {
136
		return (version & VM_PALCOL_MIRRORING) != 0;
137
	}
138
139
	/** Does this VDP have VRAM remapping when switching from 4k to 8/16k mode?
140
	  * @return True iff this is a VDP with VRAM remapping
141
	  */
142
	[[nodiscard]] bool isVDPwithVRAMremapping() const {
143
		return (version & VM_VRAM_REMAPPING) != 0;
144
	}
145
146
	/** Does this VDP support YJK display?
147
	  * @return True for V9958, false otherwise.
148
	  */
149
	[[nodiscard]] bool hasYJK() const {
150
		return (version & VM_YJK) != 0;
151
	}
152
153
	/** Get the (fixed) palette for this MSX1 VDP.
154
	  * Don't use this if it's not an MSX1 VDP!
155
	  * @return an array of 16 RGB triplets
156
	  */
157
	[[nodiscard]] std::array<std::array<uint8_t, 3>, 16> getMSX1Palette() const;
158
159
	/** Get the display mode the VDP is in.
160
	  * @return The current display mode.
161
	  */
162
	[[nodiscard]] DisplayMode getDisplayMode() const {
163
		return displayMode;
164
	}
165
166
	/** Get the VRAM object for this VDP.
167
	  */
168
	[[nodiscard]] VDPVRAM& getVRAM() {
169
		return *vram;
170
	}
171
172
	/** Are we currently superimposing?
173
	 * In case of superimpose, returns a pointer to the to-be-superimposed
174
	 * frame. Returns nullptr if superimpose is not active.
175
	 */
176
	[[nodiscard]] const RawFrame* isSuperimposing() const {
177
		// Note that bit 0 of r#0 has no effect on an V9938 or higher,
178
		// but this bit is masked out. Also note that on an MSX1, if
179
		// bit 0 of r#0 is enabled and there is no external video
180
		// source, then we lose sync.
181
		// Also note that because this property is fixed per frame we
182
		// cannot (re)calculate it from register values.
183
		return superimposing;
184
	}
185
186
	/** Get the sprite checker for this VDP.
187
	  */
188
	[[nodiscard]] SpriteChecker& getSpriteChecker() {
189
		return *spriteChecker;
190
	}
191
192
	/** Gets the current transparency setting.
193
	  * @return True iff color 0 is transparent.
194
	  */
195
	[[nodiscard]] bool getTransparency() const {
196
		return (controlRegs[8] & 0x20) == 0;
197
	}
198
199
	/** Can a sprite which has color=0 collide with some other sprite?
200
	 */
201
	[[nodiscard]] bool canSpriteColor0Collide() const {
202
		// On MSX1 (so far only tested a TMS9129(?)) sprites with
203
		// color=0 can always collide with other sprites. Though on
204
		// V99x8 (only tested V9958) collisions only occur when color 0
205
		// is not transparent. For more details see:
206
		//   https://github.com/openMSX/openMSX/issues/1198
207
		return isMSX1VDP() || !getTransparency();
208
	}
209
210
	/** Gets the current foreground color.
211
	  * @return Color index [0..15].
212
	  */
213
	[[nodiscard]] int getForegroundColor() const {
214
		return controlRegs[7] >> 4;
215
	}
216
217
	/** Gets the current background color.
218
	  * @return Color index.
219
	  *   In Graphic5 mode, the range is [0..15];
220
	  *   bits 3-2 contains the color for even pixels,
221
	  *   bits 1-0 contains the color for odd pixels.
222
	  *   In Graphic7 mode with YJK off, the range is [0..255].
223
	  *   In other modes, the range is [0..15].
224
	  */
225
	[[nodiscard]] uint8_t getRawBackgroundColor() const {
226
		return controlRegs[7];
227
	}
228
	[[nodiscard]] uint8_t getBackgroundColor() const {
229
		uint8_t reg7 = controlRegs[7];
230
		if (displayMode.getByte() == DisplayMode::GRAPHIC7) {
231
			return reg7;
232
		} else {
233
			return reg7 & 0x0F;
234
		}
235
	}
236
237
	/** Gets the current blinking color for blinking text.
238
	  * @return Color index [0..15].
239
	  */
240
	[[nodiscard]] int getBlinkForegroundColor() const {
241
		return controlRegs[12] >> 4;
242
	}
243
244
	/** Gets the current blinking color for blinking text.
245
	  * @return Color index [0..15].
246
	  */
247
	[[nodiscard]] int getBlinkBackgroundColor() const {
248
		return controlRegs[12] & 0x0F;
249
	}
250
251
	/** Gets the current blink state.
252
	  * @return True iff alternate colors / page should be displayed.
253
	  */
254
	[[nodiscard]] bool getBlinkState(int line) const {
255
		if (isFastBlinkEnabled()) {
256
			return calculateLineBlinkState(line).state;
257
		} else {
258
			return blinkState;
259
		}
260
	}
261
262
	/** Get address of pattern table (only for debugger) */
263
	[[nodiscard]] int getPatternTableBase() const {
264
		return controlRegs[4] << 11;
265
	}
266
	/** Get address of color table (only for debugger) */
267
	[[nodiscard]] int getColorTableBase() const {
268
		return (controlRegs[10] << 14) | (controlRegs[3] << 6);
269
	}
270
	/** Get address of name table (only for debugger) */
271
	[[nodiscard]] int getNameTableBase() const {
272
		return controlRegs[2] << 10;
273
	}
274
	/** Get address of pattern table (only for debugger) */
275
	[[nodiscard]] int getSpritePatternTableBase() const {
276
		return controlRegs[6] << 11;
277
	}
278
	/** Get address of color table (only for debugger) */
279
	[[nodiscard]] int getSpriteAttributeTableBase() const {
280
		return (controlRegs[11] << 15) | (controlRegs[5] << 7);
281
	}
282
	/** Get vram pointer (14-bit) (only for debugger) */
283
	[[nodiscard]] int getVramPointer() const {
284
		return vramPointer;
285
	}
286
287
	/** Gets a palette entry.
288
	  * @param index The index [0..15] in the palette.
289
	  * @return Color value in the format of the palette registers:
290
	  *   bit 10..8 is green, bit 6..4 is red and bit 2..0 is blue.
291
	  */
292
	[[nodiscard]] uint16_t getPalette(unsigned index) const {
293
		return palette[index];
294
	}
295
	[[nodiscard]] std::span<const uint16_t, 16> getPalette() const {
296
		return palette;
297
	}
298
299
	/** Sets a palette entry.
300
	  * @param index The index [0..15] in the palette.
301
	  * @param grb value in the format of the palette registers:
302
	  *   bit 10..8 is green, bit 6..4 is red and bit 2..0 is blue.
303
	  * @param time Moment in time palette change occurs.
304
	  */
305
	void setPalette(unsigned index, uint16_t grb, EmuTime time);
306
307
	/** Is the display enabled?
308
	  * Both the regular border and forced blanking by clearing
309
	  * the display enable bit are considered disabled display.
310
	  * @return true iff enabled.
311
	  */
312
	[[nodiscard]] bool isDisplayEnabled() const {
313
		return isDisplayArea && displayEnabled;
314
	}
315
316
	/** Is the VDP fetching display data, as far as the VRAM access slots are
317
	  * concerned? The access grid does not follow the display area: it starts
318
	  * 162 cycles into the line *before* it, where the VDP starts fetching the
319
	  * sprite data of the first display line, and it ends 162 cycles into the
320
	  * line after it. Measured at both borders, see section 12 of
321
	  * doc/internal/vdp-vram-timing/2026-measurement-analysis.md.
322
	  * @return true iff the display access grid applies.
323
	  */
324
	[[nodiscard]] bool isSlotTableDisplayEnabled() const {
325
		return isSlotTableDisplayArea && displayEnabled;
326
	}
327
328
	/** Are sprites enabled?
329
	  * @return True iff blanking is off, the current mode supports
330
	  *   sprites and sprites are not disabled.
331
	  */
332
	[[nodiscard]] bool spritesEnabled() const {
333
		return displayEnabled &&
334
		       (displayMode.getSpriteMode(isMSX1VDP()) != 0) &&
335
		       spriteEnabled;
336
	}
337
338
	/** Same as spritesEnabled(), but may only be called in sprite
339
	  * mode 1 or 2. Is a tiny bit faster.
340
	  */
341
	[[nodiscard]] bool spritesEnabledFast() const {
342
		assert(displayMode.getSpriteMode(isMSX1VDP()) != 0);
343
		return displayEnabled && spriteEnabled;
344
	}
345
346
	/** Still faster variant (just looks at the sprite-enabled-bit).
347
	  * But only valid in sprite mode 1/2 with screen enabled.
348
	  */
349
	[[nodiscard]] bool spritesEnabledRegister() const {
350
		return spriteEnabled;
351
	}
352
353
	/** Gets the current vertical scroll (line displayed at Y=0).
354
	  * @return Vertical scroll register value.
355
	  */
356
	[[nodiscard]] uint8_t getVerticalScroll() const {
357
		return controlRegs[23];
358
	}
359
360
	/** Gets the current horizontal scroll lower bits.
361
	  * Rather than actually scrolling the screen, this setting shifts the
362
	  * screen 0..7 bytes to the right.
363
	  * @return Horizontal scroll low register value.
364
	  */
365
	[[nodiscard]] uint8_t getHorizontalScrollLow() const {
366
		return controlRegs[27];
367
	}
368
369
	/** Gets the current horizontal scroll higher bits.
370
	  * This value determines how many 8-pixel steps the background is
371
	  * rotated to the left.
372
	  * @return Horizontal scroll high register value.
373
	  */
374
	[[nodiscard]] uint8_t getHorizontalScrollHigh() const {
375
		return controlRegs[26];
376
	}
377
378
	/** Gets the current border mask setting.
379
	  * Border mask extends the left border by 8 pixels if enabled.
380
	  * This is a V9958 feature, on older VDPs it always returns false.
381
	  * @return true iff enabled.
382
	  */
383
	[[nodiscard]] bool isBorderMasked() const {
384
		return (controlRegs[25] & 0x02) != 0;
385
	}
386
387
	/** Is multi page scrolling enabled?
388
	  * It is considered enabled if both the multi page scrolling flag is
389
	  * enabled and the current page is odd. Scrolling wraps into the
390
	  * lower even page.
391
	  * @return true iff enabled.
392
	  */
393
	[[nodiscard]] bool isMultiPageScrolling() const {
394
		return (controlRegs[25] & 0x01) && (controlRegs[2] & 0x20);
395
	}
396
397
	/** Only used by debugger
398
	  * @return page 0-3
399
	  */
400
	[[nodiscard]] int getDisplayPage() const {
401
		return (controlRegs[2] >> 5) & 3;
402
	}
403
404
	/** Get the absolute line number of display line zero.
405
	  * Usually this is equal to the height of the top border,
406
	  * but not so during overscan.
407
	  * Note: until this frame's display line counter reset (in the top
408
	  * border, see execLineCountReset()) this still refers to the previous
409
	  * frame's display start, expressed relative to this frame, so it is
410
	  * then negative.
411
	  */
412
	[[nodiscard]] int getLineZero() const {
413
		// Floor division, so that 'line - getLineZero()' keeps counting
414
		// display lines correctly while displayStart is negative.
415
		int line = displayStart / TICKS_PER_LINE;
416
		if ((displayStart % TICKS_PER_LINE) < 0) --line;
417
		return line;
418
	}
419
420
	/** Is PAL timing active?
421
	  * This setting is fixed at start of frame.
422
	  * @return True if PAL timing, false if NTSC timing.
423
	  */
424
	[[nodiscard]] bool isPalTiming() const {
425
		return palTiming;
426
	}
427
428
	/** Get interlace status.
429
	  * Interlace means the odd fields are displayed half a line lower
430
	  * than the even fields. Together with even/odd page alternation
431
	  * this can be used to create an interlaced display.
432
	  * This setting is fixed at start of frame.
433
	  * @return True iff interlace is enabled.
434
	  */
435
	[[nodiscard]] bool isInterlaced() const {
436
		return interlaced;
437
	}
438
439
	/** Get 'fast-blink' status.
440
	  * Normally blinking timing (alternating between pages) is based on
441
	  * frames. Though the V99x8 has an undocumented feature which changes
442
	  * this timing to lines. Sometimes this is called the "Cadari" feature
443
	  * after Luciano Cadari who discovered it.
444
	  *
445
	  * See ticket#1091: "Support for undocumented V99x8 register 1 bit 2"
446
	  *    https://github.com/openMSX/openMSX/issues/1091
447
	  * for test cases and links to more information.
448
	  */
449
	[[nodiscard]] bool isFastBlinkEnabled() const {
450
		return (controlRegs[1] & 4) != 0;
451
	}
452
453
	/** Get even/odd page alternation status.
454
	  * Interlace means the odd fields are displayed half a line lower
455
	  * than the even fields. Together with even/odd page alternation
456
	  * this can be used to create an interlaced display.
457
	  * This setting is NOT fixed at start of frame.
458
	  * TODO: Find out how real VDP does it.
459
	  *       If it fixes it at start of frame, so should we.
460
	  *       If it handles it dynamically (my guess), then an update method
461
	  *       should be added on the Renderer interface.
462
	  * @return True iff even/odd page alternation is enabled.
463
	  */
464
	[[nodiscard]] bool isEvenOddEnabled() const {
465
		if (isFastBlinkEnabled()) return false;
466
		return (controlRegs[9] & 4) != 0;
467
	}
468
469
	/** Is the even or odd field being displayed?
470
	  * @return True iff this field should be displayed half a line lower.
471
	  */
472
	[[nodiscard]] bool getEvenOdd() const {
473
		return (statusReg2 & 2) != 0;
474
	}
475
476
	/** Expresses the state of even/odd page interchange in a mask
477
	  * on the line number. If even/odd interchange is active, for some
478
	  * frames lines 256..511 (page 1) are replaced by 0..255 (page 0)
479
	  * and 768..1023 (page 3, if applicable) by 512..767 (page 2).
480
	  * Together with the interlace setting this can be used to create
481
	  * an interlaced display.
482
	  * Even/odd interchange can also happen because of the 'blink'
483
	  * feature in bitmap modes.
484
	  * @pre !isFastBlinkEnabled()
485
	  * @return Line number mask that expressed even/odd state.
486
	  */
487
	[[nodiscard]] unsigned getEvenOddMask() const {
488
		// TODO: Verify which page is displayed on even fields.
489
		assert(!isFastBlinkEnabled());
490
		return (((~controlRegs[9] & 4) << 6) | ((statusReg2 & 2) << 7)) &
491
		       (!blinkState << 8);
492
	}
493
494
	/** Similar to the above getEvenOddMask() method, but can also be
495
	  * called when 'isFastBlinkEnabled() == true'. In the latter case
496
	  * the timing is based on lines instead of frames. This means the
497
	  * result is no longer fixed per frame, and thus this method takes
498
	  * an additional line parameter.
499
	  */
500
	[[nodiscard]] unsigned getEvenOddMask(int line) const {
501
		if (isFastBlinkEnabled()) {
502
			// EO and IL not considered in this mode
503
			auto p = calculateLineBlinkState(line);
504
			return (!p.state) << 8;
505
		} else {
506
			return getEvenOddMask();
507
		}
508
	}
509
510
	/** Calculates what 'blinkState' and 'blinkCount' would be at a specific line.
511
	  * (The actual 'blinkState' and 'blinkCount' variables represent the values
512
	  * for line 0 and remain fixed for the duration of the frame.
513
	  */
514
	struct BlinkStateCount {
515
		bool state;
516
		int count;
517
	};
518
	[[nodiscard]] BlinkStateCount calculateLineBlinkState(unsigned line) const {
519
		assert(isFastBlinkEnabled());
520
521
		if (blinkCount == 0) { // not changing
522
			return {.state = blinkState, .count = blinkCount};
523
		}
524
525
		unsigned evenLen = ((controlRegs[13] >> 4) & 0x0F) * 10;
526
		unsigned oddLen  = ((controlRegs[13] >> 0) & 0x0F) * 10;
527
		unsigned totalLen = evenLen + oddLen;
528
		assert(totalLen != 0); // because this implies 'blinkCount == 0'
529
		line %= totalLen; // reduce double flips
530
531
		bool resultState = blinkState; // initial guess, adjusted later
532
		if (blinkState) {
533
			// We start in the 'even' period -> check first for
534
			// even/odd transition, next for odd/even
535
		} else {
536
			// We start in the 'odd' period -> do the opposite
537
			std::swap(evenLen, oddLen);
538
		}
539
		int newCount = blinkCount - narrow<int>(line);
540
		if (newCount <= 0) {
541
			// switch even->odd    (or odd->even)
542
			resultState = !resultState;
543
			newCount += narrow<int>(oddLen);
544
			if (newCount <= 0) {
545
				// switch odd->even   (or even->odd)
546
				resultState = !resultState;
547
				newCount += narrow<int>(evenLen);
548
				assert(newCount > 0);
549
			}
550
		}
551
		return {.state = resultState, .count = newCount};
552
	}
553
554
	/** Gets the number of VDP clock ticks (21MHz) elapsed between
555
	  * a given time and the start of this frame.
556
	  */
557
	[[nodiscard]] int getTicksThisFrame(EmuTime time) const {
558
		return narrow<int>(frameStartTime.getTicksTill(time));
559
	}
560
	[[nodiscard]] EmuTime getTimeInFrame(int ticks) const {
561
		return frameStartTime + ticks;
562
	}
563
564
	/** In bitmap modes the VDP fetches VRAM in bursts of 8 pixels, so a
565
	  * vertical scroll (R#23) write mid-line only takes effect at a later
566
	  * burst. Returns the frame tick at which a write at 'time' reaches the
567
	  * screen, or -1 if it takes effect immediately.
568
	  */
569
	[[nodiscard]] int getScrollBurstTicks(EmuTime time) const;
570
571
	[[nodiscard]] EmuTime getFrameStartTime() const {
572
		return frameStartTime.getTime();
573
	}
574
575
	/** For debugger only. Returns the moment in time a vblank-IRQ or
576
	  * line-IRQ will occur. This can be this frame or the next. */
577
	[[nodiscard]] std::optional<EmuTime> getVScanTime() const {
578
		if (!(controlRegs[1] & 0x20)) return {};
579
		return syncVScan.isPending();
580
	}
581
	[[nodiscard]] std::optional<EmuTime> getHScanTime() const {
582
		if (!(controlRegs[0] & 0x10)) return {};
583
		return syncHScan.isPending();
584
	}
585
586
	/** Gets the sprite size in pixels (8/16).
587
	  */
588
	[[nodiscard]] int getSpriteSize() const {
589
		return ((controlRegs[1] & 2) << 2) + 8;
590
	}
591
592
	/** Are sprites magnified?
593
	  */
594
	[[nodiscard]] bool isSpriteMag() const {
595
		return controlRegs[1] & 1;
596
	}
597
598
	/** Are commands possible in non Graphic modes? (V9958 only)
599
	  * @return True iff CMD bit set.
600
	  */
601
	[[nodiscard]] bool getCmdBit() const {
602
		return (controlRegs[25] & 0x40) != 0;
603
	}
604
605
	/** Gets the number of lines per frame.
606
	  */
607
	[[nodiscard]] int getLinesPerFrame() const {
608
		return palTiming ? PAL_LINES : NTSC_LINES;
609
	}
610
611
	/** Gets the number of display lines per screen.
612
	  * @return 192 or 212.
613
	  */
614
	[[nodiscard]] int getNumberOfLines() const {
615
		return controlRegs[9] & 0x80 ? 212 : 192;
616
	}
617
618
	/** Gets the number of VDP clock ticks (21MHz) per frame.
619
	  */
620
	[[nodiscard]] int getTicksPerFrame() const {
621
		return getLinesPerFrame() * TICKS_PER_LINE;
622
	}
623
624
	/** Gets the duration of a single frame (PAL or NTSC).
625
	 */
626
	[[nodiscard]] EmuDuration getFrameDuration() const {
627
		return VDPClock::duration(getTicksPerFrame());
628
	}
629
630
	/** Is the given timestamp inside the current frame?
631
	  * Mainly useful for debugging, because relevant timestamps should
632
	  * always be inside the current frame.
633
	  * The end time of a frame is still considered inside,
634
	  * to support the case when the given timestamp is exclusive itself:
635
	  * a typically "limit" variable.
636
	  * @param time Timestamp to check.
637
	  * @return True iff the timestamp is inside the current frame.
638
	  */
639
	[[nodiscard]] bool isInsideFrame(EmuTime time) const {
640
		return time >= frameStartTime.getTime() &&
641
			getTicksThisFrame(time) <= getTicksPerFrame();
642
	}
643
644
	/** This is a combination of the (horizontal) set adjust register and
645
	  * the YJK-mode bit.
646
	  */
647
	[[nodiscard]] int getHorizontalAdjust() const {
648
		return horizontalAdjust;
649
	}
650
651
	/** Gets the number of VDP clock ticks between start of line and the start
652
	  * of the sprite plane.
653
	  * The location of the sprite plane is not influenced by horizontal scroll
654
	  * or border mask.
655
	  * TODO: Leave out the text mode case, since there are no sprites
656
	  *       in text mode?
657
	  */
658
	[[nodiscard]] int getLeftSprites() const {
659
		// The text mode (40*6 = 240) pixels are not centered in the 256
660
		// pixels of the other modes. And it's different between TMSxxx
661
		// and V99x8. See https://github.com/openMSX/openMSX/issues/708
662
		return 100 + 102 + 56
663
			+ (horizontalAdjust - 7) * 4
664
			+ (displayMode.isTextMode() ? (isMSX1VDP() ? 6 : 9) : 0) * 4;
665
	}
666
667
	/** Gets the number of VDP clock ticks between start of line and the end
668
	  * of the left border.
669
	  * Does not include extra pixels of horizontal scroll low, since those
670
	  * are not actually border pixels (sprites appear in front of them).
671
	  */
672
	[[nodiscard]] int getLeftBorder() const {
673
		return getLeftSprites() + (isBorderMasked() ? 8 * 4 : 0);
674
	}
675
676
	/** Gets the number of VDP clock ticks between start of line and the start
677
	  * of the right border.
678
	  */
679
	[[nodiscard]] int getRightBorder() const {
680
		return getLeftSprites()
681
			+ (displayMode.isTextMode() ? 960 : 1024);
682
	}
683
684
	/** Gets the number of VDP clock ticks between start of line and the time
685
	  * when the background pixel with X coordinate 0 would be drawn.
686
	  * This includes extra pixels of horizontal scroll low,
687
	  * but disregards border mask.
688
	  */
689
	[[nodiscard]] int getLeftBackground() const {
690
		return getLeftSprites() + getHorizontalScrollLow() * 4;
691
	}
692
693
	/** Should only be used by SpriteChecker. Returns the current value
694
	  * of status register 0 (both the F-flag and the sprite related bits).
695
	  */
696
	[[nodiscard]] uint8_t getStatusReg0() const { return statusReg0; }
697
698
	/** Should only be used by SpriteChecker. Change the sprite related
699
	  * bits of status register 0 (leaves the F-flag unchanged).
700
	  * Bit 6 (5S) is set when more than 4 (sprite mode 1) or 8 (sprite
701
	  *   mode 2) sprites occur on the same line.
702
	  * Bit 5 (C) is set when sprites collide.
703
	  * Bit 4..0 (5th sprite number) contains the number of the first
704
	  *   sprite to exceed the limit per line.
705
	  */
706
	void setSpriteStatus(uint8_t value)
707
	{
708
		statusReg0 = (statusReg0 & 0x80) | (value & 0x7F);
709
	}
710
711
	/** Returns current VR mode.
712
	  * false -> VR=0,  true -> VR=1
713
	  */
714
	[[nodiscard]] bool getVRMode() const {
715
		return (controlRegs[8] & 8) != 0;
716
	}
717
718
	/** Enable superimposing
719
	  */
720
	void setExternalVideoSource(const RawFrame* externalSource) {
721
		externalVideo = externalSource;
722
	}
723
724
	/** Value of the cmdTiming setting, true means commands have infinite speed.
725
	 */
726
	[[nodiscard]] bool getBrokenCmdTiming() const {
727
		return brokenCmdTiming;
728
	}
729
730
	/** Get the earliest access slot that is at least 'delta' cycles in
731
	  * the future. */
732
	[[nodiscard]] EmuTime getAccessSlot(EmuTime time, VDPAccessSlots::Delta delta) const;
733
734
	/** Same as getAccessSlot(), but it can be _much_ faster for repeated
735
	  * calls, e.g. in the implementation of VDP commands. However it does
736
	  * have some limitations:
737
	  * - The returned calculator only remains valid for as long as
738
	  *   the VDP access timing remains the same (display/sprite enable).
739
	  * - The calculator needs to see _all_ time changes.
740
	  * (So this means that in every VDPCmd::execute() method you need
741
	  * to construct a new calculator).
742
	  */
743
	[[nodiscard]] VDPAccessSlots::Calculator getAccessSlotCalculator(
744
		EmuTime time, EmuTime limit) const;
745
746
	/** Only used when there are commandExecuting-probe listeners.
747
	 *
748
	 * Call to announce a (lower-bound) estimate for when the VDP command
749
	 * will finish executing. In response the VDP will schedule a
750
	 * synchronization point to sync with VDPCmdEngine emulation.
751
	 *
752
	 * Normally it's not required to pro-actively sync with the end of a
753
	 * VDP command. Instead these sync happen reactively on VDP status
754
	 * reads (e.g. polling the CE bit) or on VRAM reads (rendering or CPU
755
	 * VRAM read). This is in contrast with the V9990 where we DO need an
756
	 * active sync because the V9990 can generate an IRQ on command end.
757
	 *
758
	 * Though when the VDP.commandExecuting probe is in use we do want a
759
	 * reasonably timing accurate reaction of that probe. So (only) then we
760
	 * do add the extra syncs (thus with extra emulation overhead when you
761
	 * use that probe).
762
	 */
763
	void scheduleCmdSync(EmuTime t) {
764
		if (auto now = getCurrentTime(); t <= now) {
765
			// The largest amount of VDP cycles between 'progress'
766
			// in command emulation:
767
			// - worst case the LMMM takes 120+64 cycles to fully process one pixel
768
			// - the largest gap between access slots is 70 cycles
769
			// - but if we're unlucky the CPU steals that slot
770
			int LARGEST_STALL = 184 + 2 * 70;
771
772
			t = now + VDPClock::duration(LARGEST_STALL);
773
		}
774
		syncCmdDone.setSyncPoint(t);
775
	}
776
777
	/**
778
	 * Returns the position of the raster beam expressed in 'narrow' MSX
779
	 * screen coordinates (like 'screen 7').
780
	 *
781
	 * Horizontally: pixels in the left border have a negative coordinate,
782
	 * pixels in the right border have a coordinate bigger than or equal to
783
	 * 512.
784
	 * For MSX screen modes that are 256 pixels wide (e.g. 'screen 5')
785
	 * divide the x-coordinate by 2. For text modes (aka screen 0, width
786
	 * 40/80) subtract 16 from the x-coordinate (= (512-480)/2), and divide
787
	 * by 2 (for width 40).
788
	 *
789
	 * Vertically: lines in the top border have negative coordinates, lines
790
	 * in the bottom border have coordinates bigger or equal to 192 or 212.
791
	 */
792
	[[nodiscard]] gl::ivec2 getMSXPos(EmuTime time) const {
793
		auto ticks = getTicksThisFrame(time);
794
		return {((ticks % VDP::TICKS_PER_LINE) - getLeftSprites()) / 2,
795
		         (ticks / VDP::TICKS_PER_LINE) - getLineZero()};
796
	}
797
798
	// for debugging only
799
	VDPCmdEngine& getCmdEngine() { return *cmdEngine; }
800
801
	/** The frame that is currently being drawn, could be nullptr. */
802
	[[nodiscard]] const RawFrame* getWorkingFrame(EmuTime time);
803
804
	/** The last completed frame, could be nullptr. */
805
	[[nodiscard]] const RawFrame* getLastFrame() const;
806
807
	template<typename Archive>
808
	void serialize(Archive& ar, unsigned version);
809
810
private:
811
	void initTables();
812
813
	// VdpVersion bitmasks
814
	static constexpr unsigned VM_MSX1             =   1; // set-> MSX1,       unset-> MSX2 or MSX2+
815
	static constexpr unsigned VM_PAL              =   2; // set-> fixed PAL,  unset-> fixed NTSC or switchable
816
	static constexpr unsigned VM_NO_MIRRORING     =   4; // set-> no (screen2) mirroring
817
	static constexpr unsigned VM_PALCOL_MIRRORING =   8; // set-> pattern/color-table mirroring
818
	static constexpr unsigned VM_VRAM_REMAPPING   =  16; // set-> 4k,8/16k VRAM remapping
819
	static constexpr unsigned VM_TOSHIBA_PALETTE  =  32; // set-> has Toshiba palette
820
	static constexpr unsigned VM_YJK              =  64; // set-> has YJK (MSX2+)
821
	static constexpr unsigned VM_YM2220_PALETTE   = 128; // set-> has YM2220 palette
822
823
	/** VDP version: the VDP model being emulated. */
824
	enum VdpVersion : uint8_t {
825
		/** MSX1 VDP, NTSC version.
826
		  * TMS9918A has NTSC encoding built in,
827
		  * while TMS9928A has color difference output;
828
		  * in emulation there is no difference. */
829
		TMS99X8A   = VM_MSX1 | VM_PALCOL_MIRRORING | VM_VRAM_REMAPPING,
830
831
		/** MSX1 VDP, PAL version. */
832
		TMS9929A   = VM_MSX1 | VM_PALCOL_MIRRORING | VM_VRAM_REMAPPING | VM_PAL,
833
834
		/** newer variant PAL. */
835
		TMS9129    = VM_MSX1 | VM_PAL,
836
837
		/** newer variant NTSC. */
838
		TMS91X8    = VM_MSX1,
839
840
		/** Toshiba clone (hardwired as PAL). */
841
		T6950PAL   = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_NO_MIRRORING | VM_PAL,
842
843
		/** Toshiba clone (hardwired as NTSC). */
844
		T6950NTSC  = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_NO_MIRRORING,
845
846
		/** VDP in Toshiba T7937A engine (hardwired as PAL). */
847
		T7937APAL  = VM_MSX1 | VM_TOSHIBA_PALETTE | VM_PAL,
848
849
		/** VDP in Toshiba T7937A engine (hardwired as NTSC). */
850
		T7937ANTSC = VM_MSX1 | VM_TOSHIBA_PALETTE,
851
852
		/** Yamaha clone (hardwired as PAL). */
853
		YM2220PAL  = VM_MSX1 | VM_YM2220_PALETTE | VM_PALCOL_MIRRORING | VM_PAL,
854
855
		/** Yamaha clone (hardwired as NTSC). */
856
		YM2220NTSC = VM_MSX1 | VM_YM2220_PALETTE | VM_PALCOL_MIRRORING,
857
858
		/** MSX2 VDP. */
859
		V9938      = 0,
860
861
		/** MSX2+ and turbo R VDP. */
862
		V9958      = VM_YJK,
863
	};
864
865
	struct SyncBase : public Schedulable {
866
		explicit SyncBase(const VDP& vdp_) : Schedulable(vdp_.getScheduler()) {}
867
		using Schedulable::removeSyncPoint;
868
		using Schedulable::setSyncPoint;
869
	protected:
870
		~SyncBase() = default;
871
	};
872
873
	struct SyncVSync final : public SyncBase {
874
		using SyncBase::SyncBase;
875
		void executeUntil(EmuTime time) override {
876
			auto& vdp = OUTER(VDP, syncVSync);
877
			vdp.execVSync(time);
878
		}
879
	} syncVSync;
880
881
	struct SyncLineCountReset final : public SyncBase {
882
		using SyncBase::SyncBase;
883
		void executeUntil(EmuTime time) override {
884
			auto& vdp = OUTER(VDP, syncLineCountReset);
885
			vdp.execLineCountReset(time);
886
		}
887
	} syncLineCountReset;
888
889
	struct SyncDisplayStart final : public SyncBase {
890
		using SyncBase::SyncBase;
891
		void executeUntil(EmuTime time) override {
892
			auto& vdp = OUTER(VDP, syncDisplayStart);
893
			vdp.execDisplayStart(time);
894
		}
895
	} syncDisplayStart;
896
897
	struct SyncSlotTableStart final : public SyncBase {
898
		using SyncBase::SyncBase;
899
		void executeUntil(EmuTime time) override {
900
			auto& vdp = OUTER(VDP, syncSlotTableStart);
901
			vdp.execSlotTableStart(time);
902
		}
903
	} syncSlotTableStart;
904
905
	struct SyncSlotTableEnd final : public SyncBase {
906
		using SyncBase::SyncBase;
907
		void executeUntil(EmuTime time) override {
908
			auto& vdp = OUTER(VDP, syncSlotTableEnd);
909
			vdp.execSlotTableEnd(time);
910
		}
911
	} syncSlotTableEnd;
912
913
	struct SyncVScan final : public SyncBase {
914
		using SyncBase::SyncBase;
915
		void executeUntil(EmuTime time) override {
916
			auto& vdp = OUTER(VDP, syncVScan);
917
			vdp.execVScan(time);
918
		}
919
	} syncVScan;
920
921
	struct SyncHScan final : public SyncBase {
922
		using SyncBase::SyncBase;
923
		void executeUntil(EmuTime /*time*/) override {
924
			auto& vdp = OUTER(VDP, syncHScan);
925
			vdp.execHScan();
926
		}
927
	} syncHScan;
928
929
	struct SyncHorAdjust final : public SyncBase {
930
		using SyncBase::SyncBase;
931
		void executeUntil(EmuTime time) override {
932
			auto& vdp = OUTER(VDP, syncHorAdjust);
933
			vdp.execHorAdjust(time);
934
		}
935
	} syncHorAdjust;
936
937
	struct SyncSetMode final : public SyncBase {
938
		using SyncBase::SyncBase;
939
		void executeUntil(EmuTime time) override {
940
			auto& vdp = OUTER(VDP, syncSetMode);
941
			vdp.execSetMode(time);
942
		}
943
	} syncSetMode;
944
945
	struct SyncSetBlank final : public SyncBase {
946
		using SyncBase::SyncBase;
947
		void executeUntil(EmuTime time) override {
948
			auto& vdp = OUTER(VDP, syncSetBlank);
949
			vdp.execSetBlank(time);
950
		}
951
	} syncSetBlank;
952
953
	struct SyncSetSprites final : public SyncBase {
954
		using SyncBase::SyncBase;
955
		void executeUntil(EmuTime time) override {
956
			auto& vdp = OUTER(VDP, syncSetSprites);
957
			vdp.execSetSprites(time);
958
		}
959
	} syncSetSprites;
960
961
	struct SyncCpuVramAccess final : public SyncBase {
962
		using SyncBase::SyncBase;
963
		void executeUntil(EmuTime time) override {
964
			auto& vdp = OUTER(VDP, syncCpuVramAccess);
965
			vdp.execCpuVramAccess(time);
966
		}
967
	} syncCpuVramAccess;
968
969
	struct SyncCpuVramDummy final : public SyncBase {
970
		using SyncBase::SyncBase;
971
		void executeUntil(EmuTime time) override {
972
			auto& vdp = OUTER(VDP, syncCpuVramDummy);
973
			vdp.execCpuVramDummy(time);
974
		}
975
	} syncCpuVramDummy;
976
977
	struct SyncCmdDone final : public SyncBase {
978
		using SyncBase::SyncBase;
979
		void executeUntil(EmuTime time) override {
980
			auto& vdp = OUTER(VDP, syncCmdDone);
981
			vdp.execSyncCmdDone(time);
982
		}
983
	} syncCmdDone;
984
985
	void execVSync(EmuTime time);
986
	void execLineCountReset(EmuTime time);
987
	void execDisplayStart(EmuTime time);
988
	void execVScan(EmuTime time);
989
	void setSlotTableDisplayArea(bool enabled, EmuTime time);
990
	void execSlotTableStart(EmuTime time);
991
	void execSlotTableEnd(EmuTime time);
992
	void execHScan();
993
	void execHorAdjust(EmuTime time);
994
	void execSetMode(EmuTime time);
995
	void execSetBlank(EmuTime time);
996
	void execSetSprites(EmuTime time);
997
	void execCpuVramAccess(EmuTime time);
998
	void execSyncCmdDone(EmuTime time);
999
1000
	/** Returns the amount of vertical set-adjust 0..15.
1001
	  * Neutral set-adjust (that is 'set adjust(0,0)') returns the value '7'.
1002
	  */
1003
	[[nodiscard]] int getVerticalAdjust() const {
1004
		return (controlRegs[18] >> 4) ^ 0x07;
1005
	}
1006
1007
	/** Gets the value of the horizontal retrace status bit.
1008
	  * Note that HR flipping continues at all times, not just during
1009
	  * vertical display range.
1010
	  * @param ticksThisFrame The screen position (in VDP ticks)
1011
	  *    to return HR for.
1012
	  * @return True iff the VDP scanning is inside the left/right
1013
	  *   border or left/right erase or horizontal sync.
1014
	  *   False iff the VDP scanning is in the display range.
1015
	  */
1016
	[[nodiscard]] bool getHR(int ticksThisFrame) const {
1017
		// Note: These constants are located inside this function because
1018
		//       GCC 4.0.x won't link if they are in the class scope.
1019
		/** Length of horizontal blank (HR=1) in text mode, measured in VDP
1020
		  * ticks.
1021
		  */
1022
		static constexpr int HBLANK_LEN_TXT = 404;
1023
		/** Length of horizontal blank (HR=1) in graphics mode, measured in VDP
1024
		  * ticks.
1025
		  */
1026
		static constexpr int HBLANK_LEN_GFX = 312;
1027
		return (ticksThisFrame + TICKS_PER_LINE - getRightBorder()) % TICKS_PER_LINE
1028
		     < (displayMode.isTextMode() ? HBLANK_LEN_TXT : HBLANK_LEN_GFX);
1029
	}
1030
1031
	// VideoSystemChangeListener interface:
1032
	void preVideoSystemChange() noexcept override;
1033
	void postVideoSystemChange() noexcept override;
1034
1035
	/** Called both on init and on reset.
1036
	  * Puts VDP into reset state.
1037
	  * Does not call any renderer methods.
1038
	  */
1039
	void resetInit();
1040
1041
	/** Companion to resetInit: in resetInit the registers are reset,
1042
	  * in this method the new base masks are distributed to the VDP
1043
	  * subsystems.
1044
	  */
1045
	void resetMasks(EmuTime time);
1046
1047
	/** Start a new frame.
1048
	  * @param time The moment in emulated time the frame starts.
1049
	  */
1050
	void frameStart(EmuTime time);
1051
1052
	/** Schedules the display line counter reset for this frame, at line
1053
	  * 8 + vertical adjust. Also removes a pending one, if any. The reset
1054
	  * is where DISPLAY_START, VSCAN and HSCAN get scheduled from, see
1055
	  * execLineCountReset().
1056
	  * @param time The moment in emulated time this call takes place.
1057
	  *   Note: time is not the reset sync time!
1058
	  */
1059
	void scheduleLineCountReset(EmuTime time);
1060
1061
	/** Schedules a VSCAN sync point.
1062
	  * Also removes a pending VSCAN sync, if any.
1063
	  * @param time The moment in emulated time this call takes place.
1064
	  *   Note: time is not the VSCAN sync time!
1065
	  */
1066
	void scheduleVScan(EmuTime time);
1067
1068
	/** Schedules a HSCAN sync point.
1069
	  * Also removes a pending HSCAN sync, if any.
1070
	  * @param time The moment in emulated time this call takes place.
1071
	  *   Note: time is not the HSCAN sync time!
1072
	  */
1073
	void scheduleHScan(EmuTime time);
1074
1075
	/** Byte is written to VRAM by the CPU.
1076
	  */
1077
	void vramWrite(uint8_t value, EmuTime time);
1078
1079
	/** Byte is read from VRAM by the CPU.
1080
	  */
1081
	[[nodiscard]] uint8_t vramRead(EmuTime time);
1082
1083
	/** Helper methods for CPU-VRAM access. */
1084
	void scheduleCpuVramAccess(bool isRead, uint8_t write, EmuTime time);
1085
	void execCpuVramDummy(EmuTime time);
1086
	void scheduleTMS99x8VramAccess(bool isRead, EmuTime time);
1087
	void scheduleV99x8VramAccess(bool isRead, EmuTime time);
1088
	[[nodiscard]] bool cpuRequestIsTooEarly(EmuTime request) const;
1089
	void flushCpuVramAccesses(EmuTime time);
1090
	void executeCpuVramAccess(EmuTime time);
1091
1092
	/** Read the contents of a status register
1093
	  */
1094
	[[nodiscard]] uint8_t readStatusReg(uint8_t reg, EmuTime time);
1095
1096
	/** Schedule a sync point at the start of the next line.
1097
	  */
1098
	void syncAtNextLine(SyncBase& type, EmuTime time) const;
1099
1100
	/** Create a new renderer.
1101
	  */
1102
	void createRenderer();
1103
1104
	/** Name base mask has changed.
1105
	  * Inform the renderer and the VRAM.
1106
	  */
1107
	void updateNameBase(EmuTime time);
1108
1109
	/** Color base mask has changed.
1110
	  * Inform the renderer and the VRAM.
1111
	  */
1112
	void updateColorBase(EmuTime time);
1113
1114
	/** Pattern base mask has changed.
1115
	  * Inform the renderer and the VRAM.
1116
	  */
1117
	void updatePatternBase(EmuTime time);
1118
1119
	/** Sprite attribute base mask has changed.
1120
	  * Inform the SpriteChecker and the VRAM.
1121
	  */
1122
	void updateSpriteAttributeBase(EmuTime time);
1123
1124
	/** Sprite pattern base mask has changed.
1125
	  * Inform the SpriteChecker and the VRAM.
1126
	  */
1127
	void updateSpritePatternBase(EmuTime time);
1128
1129
	/** Display mode has changed.
1130
	  * Update displayMode's value and inform the Renderer.
1131
	  */
1132
	void updateDisplayMode(DisplayMode newMode, bool cmdBit, EmuTime time);
1133
1134
	// Observer<Setting>
1135
	void update(const Setting& setting) noexcept override;
1136
1137
private:
1138
	Display& display;
1139
	EnumSetting<bool>& cmdTiming;
1140
	EnumSetting<bool>& tooFastAccess;
1141
1142
	struct RegDebug final : SimpleDebuggable {
1143
		explicit RegDebug(const VDP& vdp);
1144
		[[nodiscard]] uint8_t read(unsigned address, EmuTime time) override;
1145
		void write(unsigned address, uint8_t value, EmuTime time) override;
1146
	} vdpRegDebug;
1147
1148
	struct StatusRegDebug final : SimpleDebuggable {
1149
		explicit StatusRegDebug(const VDP& vdp);
1150
		[[nodiscard]] uint8_t read(unsigned address, EmuTime time) override;
1151
	} vdpStatusRegDebug;
1152
1153
	struct PaletteDebug final : SimpleDebuggable {
1154
		explicit PaletteDebug(const VDP& vdp);
1155
		[[nodiscard]] uint8_t read(unsigned address) override;
1156
		void write(unsigned address, uint8_t value, EmuTime time) override;
1157
	} vdpPaletteDebug;
1158
1159
	struct VRAMPointerDebug final : SimpleDebuggable {
1160
		explicit VRAMPointerDebug(const VDP& vdp);
1161
		[[nodiscard]] uint8_t read(unsigned address) override;
1162
		void write(unsigned address, uint8_t value, EmuTime time) override;
1163
	} vramPointerDebug;
1164
1165
	struct RegisterLatchStatusDebug final : SimpleDebuggable {
1166
		explicit RegisterLatchStatusDebug(const VDP& vdp);
1167
		[[nodiscard]] uint8_t read(unsigned address) override;
1168
	} registerLatchStatusDebug;
1169
1170
	struct VramAccessStatusDebug final : SimpleDebuggable {
1171
		explicit VramAccessStatusDebug(const VDP& vdp);
1172
		[[nodiscard]] uint8_t read(unsigned address) override;
1173
	} vramAccessStatusDebug;
1174
1175
	struct PaletteLatchStatusDebug final : SimpleDebuggable {
1176
		explicit PaletteLatchStatusDebug(const VDP& vdp);
1177
		[[nodiscard]] uint8_t read(unsigned address) override;
1178
	} paletteLatchStatusDebug;
1179
1180
	struct DataLatchDebug final : SimpleDebuggable {
1181
		explicit DataLatchDebug(const VDP& vdp);
1182
		[[nodiscard]] uint8_t read(unsigned address) override;
1183
	} dataLatchDebug;
1184
1185
	class Info : public InfoTopic {
1186
	public:
1187
		void execute(std::span<const TclObject> tokens,
1188
		             TclObject& result) const override;
1189
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
1190
		[[nodiscard]] virtual int calc(EmuTime time) const = 0;
1191
	protected:
1192
		Info(VDP& vdp_, const std::string& name, std::string helpText_);
1193
		~Info() = default;
1194
		VDP& vdp;
1195
		const std::string helpText;
1196
	};
1197
1198
	struct FrameCountInfo final : Info {
1199
		explicit FrameCountInfo(VDP& vdp);
1200
		[[nodiscard]] int calc(EmuTime time) const override;
1201
	} frameCountInfo;
1202
1203
	struct CycleInFrameInfo final : Info {
1204
		explicit CycleInFrameInfo(VDP& vdp);
1205
		[[nodiscard]] int calc(EmuTime time) const override;
1206
	} cycleInFrameInfo;
1207
1208
	struct LineInFrameInfo final : Info {
1209
		explicit LineInFrameInfo(VDP& vdp);
1210
		[[nodiscard]] int calc(EmuTime time) const override;
1211
	} lineInFrameInfo;
1212
1213
	struct CycleInLineInfo final : Info {
1214
		explicit CycleInLineInfo(VDP& vdp);
1215
		[[nodiscard]] int calc(EmuTime time) const override;
1216
	} cycleInLineInfo;
1217
1218
	struct MsxYPosInfo final : Info {
1219
		explicit MsxYPosInfo(VDP& vdp);
1220
		[[nodiscard]] int calc(EmuTime time) const override;
1221
	} msxYPosInfo;
1222
1223
	struct MsxX256PosInfo final : Info {
1224
		explicit MsxX256PosInfo(VDP& vdp);
1225
		[[nodiscard]] int calc(EmuTime time) const override;
1226
	} msxX256PosInfo;
1227
1228
	struct MsxX512PosInfo final : Info {
1229
		explicit MsxX512PosInfo(VDP& vdp);
1230
		[[nodiscard]] int calc(EmuTime time) const override;
1231
	} msxX512PosInfo;
1232
1233
	/** Renderer that converts this VDP's state into an image.
1234
	  */
1235
	std::unique_ptr<Renderer> renderer;
1236
1237
	/** Command engine: the part of the V9938/58 that executes commands.
1238
	  */
1239
	std::unique_ptr<VDPCmdEngine> cmdEngine;
1240
1241
	/** Sprite checker: calculates sprite patterns and collisions.
1242
	  */
1243
	std::unique_ptr<SpriteChecker> spriteChecker;
1244
1245
	/** VRAM management object.
1246
	  */
1247
	std::unique_ptr<VDPVRAM> vram;
1248
1249
	/** Is there an external video source which we must superimpose
1250
	  * upon?
1251
	  */
1252
	const RawFrame* externalVideo;
1253
1254
	/** Are we currently superimposing?
1255
	 * This is a combination of the 'externalVideo' member (see above) and
1256
	 * the superimpose-enable bit in VDP register R#0. This property only
1257
	 * changes at most once per frame (at the beginning of the frame).
1258
	 */
1259
	const RawFrame* superimposing;
1260
1261
	/** The emulation time when this frame was started (vsync).
1262
	  */
1263
	VDPClock frameStartTime;
1264
1265
	/** Manages vertical scanning interrupt request.
1266
	  */
1267
	OptionalIRQHelper irqVertical;
1268
1269
	/** Manages horizontal scanning interrupt request.
1270
	  */
1271
	OptionalIRQHelper irqHorizontal;
1272
1273
	/** Time of last set display line counter reset sync point.
1274
	  * Before it, in the top border, the counter still belongs to the
1275
	  * previous frame.
1276
	  */
1277
	EmuTime lineCountResetSyncTime;
1278
1279
	/** Time of last set DISPLAY_START sync point.
1280
	  */
1281
	EmuTime displayStartSyncTime;
1282
1283
	/** Moments the access grid switches to and from the display tables:
1284
	  * 1408 cycles before 'displayStartSyncTime' and 40 cycles before
1285
	  * 'vScanSyncTime'. See isSlotTableDisplayEnabled(). */
1286
	EmuTime slotTableStartSyncTime;
1287
	EmuTime slotTableEndSyncTime;
1288
1289
	/** Time of last set VSCAN sync point.
1290
	  */
1291
	EmuTime vScanSyncTime;
1292
1293
	/** Time of last set HSCAN sync point.
1294
	  */
1295
	EmuTime hScanSyncTime;
1296
1297
	TclCallback tooFastCallback;
1298
	TclCallback dotClockDirectionCallback;
1299
1300
	/** VDP version.
1301
	  */
1302
	VdpVersion version;
1303
1304
	/** Saturation of Pr component of TMS9XXXA output circuitry.
1305
	  * The output of the VDP and the circuitry between the output and the
1306
	  * output connector influences this value. Percentage in range [0:100]
1307
	  */
1308
	int saturationPr;
1309
1310
	/** Saturation of Pb component of TMS9XXXA output circuitry.
1311
	  * The output of the VDP and the circuitry between the output and the
1312
	  * output connector influences this value. Percentage in range [0:100]
1313
	  */
1314
	int saturationPb;
1315
1316
	/** The number of already fully rendered frames.
1317
	  * Not used for actual emulation, only for 'frame_count' info topic.
1318
	  * This value cannot be calculated from EmuTime because framerate is
1319
	  * not constant (PAL/NTSC).
1320
	  */
1321
	int frameCount;
1322
1323
	/** VDP ticks between start of frame and start of display, which is
1324
	  * where the display line counter reaches zero. Latched at the counter
1325
	  * reset in the top border (execLineCountReset()) and not changed
1326
	  * afterwards; from the next frame start until that frame's reset it
1327
	  * still describes the previous frame's display start, so it is then
1328
	  * negative.
1329
	  */
1330
	int displayStart;
1331
1332
	/** VDP ticks between start of frame and the moment horizontal
1333
	  * scan match occurs. Like displayStart, this can point before the
1334
	  * start of this frame or beyond its end.
1335
	  */
1336
	int horizontalScanOffset;
1337
1338
	/** Horizontal display adjust.
1339
	  * This value is update at the start of a line.
1340
	  */
1341
	int horizontalAdjust;
1342
1343
	/** Control registers.
1344
	  */
1345
	std::array<uint8_t, 32> controlRegs;
1346
1347
	/** Mask on the control register index:
1348
	  * makes MSX2 registers inaccessible on MSX1,
1349
	  * instead the MSX1 registers are mirrored.
1350
	  */
1351
	uint8_t controlRegMask;
1352
1353
	/** Mask on the values of control registers.
1354
	  * This saves a lot of masking when using the register values,
1355
	  * because it is guaranteed non-existant bits are always zero.
1356
	  * It also disables access to VDP features on a VDP model
1357
	  * which does not support those features.
1358
	  */
1359
	std::array<uint8_t, 32> controlValueMasks;
1360
1361
	/** Blinking count: number of frames until next state.
1362
	  * If the ON or OFF period is 0, blinkCount is fixed to 0.
1363
	  */
1364
	int blinkCount;
1365
1366
	/** VRAM read/write access pointer.
1367
	  * Contains the lower 14 bits of the current VRAM access address.
1368
	  */
1369
	int vramPointer;
1370
1371
	/** V9938 palette.
1372
	  */
1373
	std::array<uint16_t, 16> palette;
1374
1375
	/** Is the current scan position inside the display area?
1376
	  */
1377
	bool isDisplayArea;
1378
1379
	/** Same, but for the VRAM access slot tables, which switch one line
1380
	  * earlier. See isSlotTableDisplayEnabled(). */
1381
	bool isSlotTableDisplayArea;
1382
1383
	/** Is PAL timing active? False means NTSC timing.
1384
	  * This value is updated at the start of every frame,
1385
	  * to avoid problems with mid-screen PAL/NTSC switching.
1386
	  * @see isPalTiming.
1387
	  */
1388
	bool palTiming;
1389
1390
	/** Is interlace active?
1391
	  * @see isInterlaced.
1392
	  */
1393
	bool interlaced = false;
1394
1395
	/** Status register 0.
1396
	  * Both the F flag (bit 7) and the sprite related bits (bits 6-0)
1397
	  * are stored here.
1398
	  */
1399
	uint8_t statusReg0;
1400
1401
	/** Status register 1.
1402
	  * Bit 7 and 6 are always zero because light pen is not implemented.
1403
	  * Bit 0 is always zero; its calculation depends on IE1.
1404
	  * So all that remains is the version number.
1405
	  */
1406
	uint8_t statusReg1;
1407
1408
	/** Status register 2.
1409
	  * Bit 7, 4 and 0 of this field are always zero,
1410
	  * their value can be retrieved from the command engine.
1411
	  */
1412
	uint8_t statusReg2;
1413
1414
	/** Blinking state: should alternate color / page be displayed?
1415
	  */
1416
	bool blinkState;
1417
1418
	/** First byte written through port #99, #9A or #9B.
1419
	  */
1420
	uint8_t dataLatch;
1421
1422
	/** Direction of VRAM access for reading or writing
1423
	  * Note: this variable is _only_ used for the 'VRAM access status' debuggable.
1424
	  *   The real VDP allows to setup a read/write VRAM address and then do the opposite
1425
	  *   out/in operation.
1426
	  * See the variables 'cpuVramData' and 'cpuVramReqIsRead' for more details.
1427
	  */
1428
	bool writeAccess;
1429
1430
	/** Does the data latch have register data (port #99) stored?
1431
	  */
1432
	bool registerDataStored;
1433
1434
	/** Does the data latch have palette data (port #9A) stored?
1435
	  */
1436
	bool paletteDataStored;
1437
1438
	/** VRAM is read as soon as VRAM pointer changes.
1439
	  * TODO: Is this actually what happens?
1440
	  *   On TMS9928A the VRAM interface is the only access method.
1441
	  *   But on V9938/58 there are other ways to access VRAM;
1442
	  *   I wonder if they are consistent with this implementation.
1443
	  * This also holds the soon-to-be-written data for CPU-VRAM writes.
1444
	  */
1445
	uint8_t cpuVramData;
1446
1447
	/** CPU-VRAM requests are not executed immediately (though soon). These
1448
	  * variables describe the pending request: read or write, and for a
1449
	  * write the byte to store. That byte cannot be taken from 'cpuVramData'
1450
	  * at the time of the access, because the CPU may by then have put the
1451
	  * next request's byte there (see 'secondCpuSlot').
1452
	  */
1453
	bool cpuVramReqIsRead;
1454
	uint8_t cpuVramReqData;
1455
	bool pendingCpuAccess; // always equal to syncCpuVramAccess.isPending()
1456
1457
	/** The slot granted to the most recently accepted CPU-VRAM request, and
1458
	  * its class. It stays valid after that access has taken place, because
1459
	  * the V9938 has only one request buffer and the next request has to
1460
	  * clear this slot to be accepted at all. See cpuRequestIsTooEarly().
1461
	  * 'zero' means no request has been accepted yet, which works because no
1462
	  * request can arrive within 5 cycles of the start of the emulation. */
1463
	EmuTime previousCpuSlot = EmuTime::zero();
1464
	bool previousCpuSlotIsLate = false;
1465
1466
	/** A request accepted while the access before it has not taken place yet
1467
	  * is booked here, until that earlier access frees the sync point.
1468
	  * 'infinity' means there is no such request; there can never be more
1469
	  * than one. See scheduleV99x8VramAccess(). */
1470
	EmuTime secondCpuSlot = EmuTime::infinity();
1471
	bool secondCpuVramReqIsRead = false;
1472
	uint8_t secondCpuVramReqData = 0;
1473
1474
	/** Does CPU interface access main VRAM (false) or extended VRAM (true)?
1475
	  * This is determined by MXC (R#45, bit 6).
1476
	  */
1477
	bool cpuExtendedVram;
1478
1479
	/** Current display mode. Note that this is not always the same as the
1480
	  * display mode that can be obtained by combining the different mode
1481
	  * bits because a mode change only takes place at the start of the
1482
	  * next line.
1483
	  */
1484
	DisplayMode displayMode;
1485
1486
	/** Is display enabled. Note that this is not always the same as bit 6
1487
	  * of R#1 because the display enable status change only takes place at
1488
	  * the start of the next line.
1489
	  */
1490
	bool displayEnabled;
1491
1492
	/** Are sprites enabled. This only reflects the SPD bit in R#8. It does
1493
	  * not take screen mode, screen enabled or vertical borders into
1494
	  * account. It's not identical to the SPD bit because this variable is
1495
	  * only updated at the start of the next line.
1496
	  */
1497
	bool spriteEnabled;
1498
1499
	/** Has a warning been printed.
1500
	  * This is set when a warning about setting the dot clock direction
1501
	  * is printed.  */
1502
	bool warningPrinted = false;
1503
1504
	/** Cached version of cmdTiming/tooFastAccess setting. */
1505
	bool brokenCmdTiming;
1506
	bool allowTooFastAccess;
1507
1508
	/** Cached CPU reference */
1509
	MSXCPU& cpu;
1510
	const uint8_t fixedVDPIOdelayCycles;
1511
};
1512
SERIALIZE_CLASS_VERSION(VDP, 14);
1513
1514
} // namespace openmsx
1515
1516
#endif