openMSX
PixelRenderer.cc
Go to the documentation of this file.
1/*
2TODO:
3- Implement blinking (of page mask) in bitmap modes.
4*/
5
6#include "PixelRenderer.hh"
7#include "Rasterizer.hh"
8#include "PostProcessor.hh"
9#include "Display.hh"
10#include "VideoSystem.hh"
11#include "RenderSettings.hh"
12#include "VideoSourceSetting.hh"
13#include "IntegerSetting.hh"
14#include "VDP.hh"
15#include "VDPVRAM.hh"
16#include "SpriteChecker.hh"
17#include "EventDistributor.hh"
18#include "Event.hh"
19#include "RealTime.hh"
20#include "SpeedManager.hh"
21#include "ThrottleManager.hh"
22#include "GlobalSettings.hh"
23#include "MSXMotherBoard.hh"
24#include "Reactor.hh"
25#include "Timer.hh"
26#include "narrow.hh"
27#include "one_of.hh"
28#include "unreachable.hh"
29#include <algorithm>
30#include <cassert>
31#include <cmath>
32
33namespace openmsx {
34
35void PixelRenderer::draw(
36 int startX, int startY, int endX, int endY, DrawType drawType, bool atEnd)
37{
38 if (drawType == DRAW_BORDER) {
39 rasterizer->drawBorder(startX, startY, endX, endY);
40 } else {
41 assert(drawType == DRAW_DISPLAY);
42
43 // Calculate display coordinates.
44 int zero = vdp.getLineZero();
45 int displayX = (startX - vdp.getLeftSprites()) / 2;
46 int displayY = startY - zero;
47 if (!vdp.getDisplayMode().isTextMode()) {
48 displayY += vdp.getVerticalScroll();
49 } else {
50 // this is not what the real VDP does, but it is good
51 // enough for "Boring scroll" demo part of "Relax"
52 displayY = (displayY & 7) | (textModeCounter * 8);
53 if (atEnd && (drawType == DRAW_DISPLAY)) {
54 int low = std::max(0, (startY - zero)) / 8;
55 int high = std::max(0, (endY - zero)) / 8;
56 textModeCounter += (high - low);
57 }
58 }
59
60 displayY &= 255; // Page wrap.
61 int displayWidth = (endX - (startX & ~1)) / 2;
62 int displayHeight = endY - startY;
63
64 assert(0 <= displayX);
65 assert(displayX + displayWidth <= 512);
66
67 rasterizer->drawDisplay(
68 startX, startY,
69 displayX - vdp.getHorizontalScrollLow() * 2, displayY,
70 displayWidth, displayHeight
71 );
72 if (vdp.spritesEnabled() && !renderSettings.getDisableSprites()) {
73 rasterizer->drawSprites(
74 startX, startY,
75 displayX / 2, displayY,
76 (displayWidth + 1) / 2, displayHeight);
77 }
78 }
79}
80
81void PixelRenderer::subdivide(
82 int startX, int startY, int endX, int endY, int clipL, int clipR,
83 DrawType drawType)
84{
85 // Partial first line.
86 if (startX > clipL) {
87 bool atEnd = (startY != endY) || (endX >= clipR);
88 if (startX < clipR) {
89 draw(startX, startY, (atEnd ? clipR : endX),
90 startY + 1, drawType, atEnd);
91 }
92 if (startY == endY) return;
93 startY++;
94 }
95 // Partial last line.
96 bool drawLast = false;
97 if (endX >= clipR) {
98 endY++;
99 } else if (endX > clipL) {
100 drawLast = true;
101 }
102 // Full middle lines.
103 if (startY < endY) {
104 draw(clipL, startY, clipR, endY, drawType, true);
105 }
106 // Actually draw last line if necessary.
107 // The point of keeping top-to-bottom draw order is that it increases
108 // the locality of memory references, which generally improves cache
109 // hit rates.
110 if (drawLast) draw(clipL, endY, endX, endY + 1, drawType, false);
111}
112
114 : vdp(vdp_), vram(vdp.getVRAM())
115 , eventDistributor(vdp.getReactor().getEventDistributor())
116 , realTime(vdp.getMotherBoard().getRealTime())
117 , speedManager(
118 vdp.getReactor().getGlobalSettings().getSpeedManager())
119 , throttleManager(
120 vdp.getReactor().getGlobalSettings().getThrottleManager())
121 , renderSettings(display.getRenderSettings())
122 , videoSourceSetting(vdp.getMotherBoard().getVideoSource())
123 , spriteChecker(vdp.getSpriteChecker())
124 , rasterizer(display.getVideoSystem().createRasterizer(vdp))
125{
126 // In case of loadstate we can't yet query any state from the VDP
127 // (because that object is not yet fully deserialized). But
128 // VDP::serialize() will call Renderer::reInit() again when it is
129 // safe to query.
130 reInit();
131
132 renderSettings.getMaxFrameSkipSetting().attach(*this);
133 renderSettings.getMinFrameSkipSetting().attach(*this);
134}
135
137{
138 renderSettings.getMinFrameSkipSetting().detach(*this);
139 renderSettings.getMaxFrameSkipSetting().detach(*this);
140}
141
143{
144 return rasterizer->getPostProcessor();
145}
146
148{
149 // Don't draw before frameStart() is called.
150 // This for example can happen after a loadstate or after switching
151 // renderer in the middle of a frame.
152 renderFrame = false;
153 paintFrame = false;
154
155 rasterizer->reset();
156 displayEnabled = vdp.isDisplayEnabled();
157}
158
159void PixelRenderer::updateDisplayEnabled(bool enabled, EmuTime::param time)
160{
161 sync(time, true);
162 displayEnabled = enabled;
163}
164
165void PixelRenderer::frameStart(EmuTime::param time)
166{
167 if (!rasterizer->isActive()) {
168 frameSkipCounter = 999.0f;
169 renderFrame = false;
170 prevRenderFrame = false;
171 paintFrame = false;
172 return;
173 }
174
175 prevRenderFrame = renderFrame;
176 if (vdp.isInterlaced() && renderSettings.getDeinterlace()
177 && vdp.getEvenOdd() && vdp.isEvenOddEnabled()) {
178 // Deinterlaced odd frame: do same as even frame.
179 paintFrame = prevRenderFrame;
180 } else if (throttleManager.isThrottled()) {
181 // Note: min/maxFrameSkip control the number of skipped frames, but
182 // for every series of skipped frames there is also one painted
183 // frame, so our boundary checks are offset by one.
184 auto counter = narrow_cast<int>(frameSkipCounter);
185 if (counter < renderSettings.getMinFrameSkip()) {
186 paintFrame = false;
187 } else if (counter > renderSettings.getMaxFrameSkip()) {
188 paintFrame = true;
189 } else {
190 paintFrame = realTime.timeLeft(
191 unsigned(finishFrameDuration), time);
192 }
193 frameSkipCounter += 1.0f / float(speedManager.getSpeed());
194 } else {
195 // We need to render a frame every now and then,
196 // to show the user what is happening.
197 paintFrame = (Timer::getTime() - lastPaintTime) >= 100000; // 10 fps
198 }
199
200 if (paintFrame) {
201 frameSkipCounter = std::remainder(frameSkipCounter, 1.0f);
202 } else if (!rasterizer->isRecording()) {
203 renderFrame = false;
204 return;
205 }
206 renderFrame = true;
207
208 rasterizer->frameStart(time);
209
210 accuracy = renderSettings.getAccuracy();
211
212 nextX = 0;
213 nextY = 0;
214 // This is not what the real VDP does, but it is good enough
215 // for the "Boring scroll" demo part of ANMA's "Relax" demo.
216 textModeCounter = 0;
217}
218
219void PixelRenderer::frameEnd(EmuTime::param time)
220{
221 if (renderFrame) {
222 // Render changes from this last frame.
223 sync(time, true);
224
225 // Let underlying graphics system finish rendering this frame.
226 auto time1 = Timer::getTime();
227 rasterizer->frameEnd();
228 auto time2 = Timer::getTime();
229 auto current = narrow_cast<float>(time2 - time1);
230 const float ALPHA = 0.2f;
231 finishFrameDuration = finishFrameDuration * (1 - ALPHA) +
232 current * ALPHA;
233
234 if (vdp.isInterlaced() && vdp.isEvenOddEnabled()
235 && renderSettings.getDeinterlace() && !prevRenderFrame) {
236 // Don't paint in deinterlace mode when previous frame
237 // was not rendered.
238 paintFrame = false;
239 }
240 if (paintFrame) {
241 lastPaintTime = time2;
242 }
243 }
244 if (vdp.getMotherBoard().isActive() &&
246 eventDistributor.distributeEvent(FinishFrameEvent(
247 rasterizer->getPostProcessor()->getVideoSource(),
248 videoSourceSetting.getSource(),
249 !paintFrame));
250 }
251}
252
254 byte scroll, EmuTime::param time)
255{
256 if (displayEnabled) sync(time);
257 rasterizer->setHorizontalScrollLow(scroll);
258}
259
261 byte /*scroll*/, EmuTime::param time)
262{
263 if (displayEnabled) sync(time);
264}
265
267 bool masked, EmuTime::param time)
268{
269 if (displayEnabled) sync(time);
270 rasterizer->setBorderMask(masked);
271}
272
274 bool /*multiPage*/, EmuTime::param time)
275{
276 if (displayEnabled) sync(time);
277}
278
280 bool enabled, EmuTime::param time)
281{
282 if (displayEnabled) sync(time);
283 rasterizer->setTransparency(enabled);
284}
285
287 const RawFrame* videoSource, EmuTime::param time)
288{
289 if (displayEnabled) sync(time);
290 rasterizer->setSuperimposeVideoFrame(videoSource);
291}
292
294 byte /*color*/, EmuTime::param time)
295{
296 if (displayEnabled) sync(time);
297}
298
300 byte color, EmuTime::param time)
301{
302 sync(time);
303 rasterizer->setBackgroundColor(color);
304}
305
307 byte /*color*/, EmuTime::param time)
308{
309 if (displayEnabled) sync(time);
310}
311
313 byte /*color*/, EmuTime::param time)
314{
315 if (displayEnabled) sync(time);
316}
317
319 bool /*enabled*/, EmuTime::param /*time*/)
320{
321 // TODO: When the sync call is enabled, the screen flashes on
322 // every call to this method.
323 // I don't know why exactly, but it's probably related to
324 // being called at frame start.
325 //sync(time);
326}
327
329 unsigned index, int grb, EmuTime::param time)
330{
331 if (displayEnabled) {
332 sync(time);
333 } else {
334 // Only sync if border color changed.
335 DisplayMode mode = vdp.getDisplayMode();
336 if (mode.getBase() == DisplayMode::GRAPHIC5) {
337 auto bgColor = vdp.getBackgroundColor();
338 if (index == one_of(uint8_t(bgColor & 3), uint8_t(bgColor >> 2))) {
339 sync(time);
340 }
341 } else if (mode.getByte() != DisplayMode::GRAPHIC7) {
342 if (index == vdp.getBackgroundColor()) {
343 sync(time);
344 }
345 }
346 }
347 rasterizer->setPalette(index, grb);
348}
349
351 int /*scroll*/, EmuTime::param time)
352{
353 if (displayEnabled) sync(time);
354}
355
357 int adjust, EmuTime::param time)
358{
359 if (displayEnabled) sync(time);
360 rasterizer->setHorizontalAdjust(adjust);
361}
362
364 DisplayMode mode, EmuTime::param time)
365{
366 // Sync if in display area or if border drawing process changes.
367 DisplayMode oldMode = vdp.getDisplayMode();
368 if (displayEnabled
369 || oldMode.getByte() == DisplayMode::GRAPHIC5
370 || oldMode.getByte() == DisplayMode::GRAPHIC7
371 || mode.getByte() == DisplayMode::GRAPHIC5
372 || mode.getByte() == DisplayMode::GRAPHIC7) {
373 sync(time, true);
374 }
375 rasterizer->setDisplayMode(mode);
376}
377
379 unsigned /*addr*/, EmuTime::param time)
380{
381 if (displayEnabled) sync(time);
382}
383
385 unsigned /*addr*/, EmuTime::param time)
386{
387 if (displayEnabled) sync(time);
388}
389
391 unsigned /*addr*/, EmuTime::param time)
392{
393 if (displayEnabled) sync(time);
394}
395
397 bool /*enabled*/, EmuTime::param time
398) {
399 if (displayEnabled) sync(time);
400}
401
402static constexpr bool overlap(
403 int displayY0, // start of display region, inclusive
404 int displayY1, // end of display region, exclusive
405 int vramLine0, // start of VRAM region, inclusive
406 int vramLine1 // end of VRAM region, exclusive
407 // Note: Display region can wrap around: 256 -> 0.
408 // VRAM region cannot wrap around.
409) {
410 if (displayY0 <= displayY1) {
411 if ((vramLine1 > displayY0) && (vramLine0 <= displayY1)) {
412 return true;
413 }
414 } else {
415 if (vramLine1 > displayY0) return true;
416 if (vramLine0 <= displayY1) return true;
417 }
418 return false;
419}
420
421bool PixelRenderer::checkSync(unsigned offset, EmuTime::param time) const
422{
423 // TODO: Because range is entire VRAM, offset == address.
424
425 // If display is disabled, VRAM changes will not affect the
426 // renderer output, therefore sync is not necessary.
427 // TODO: Have bitmapVisibleWindow disabled in this case.
428 if (!displayEnabled) return false;
429 //if (frameSkipCounter != 0) return false; // TODO
430 if (accuracy == RenderSettings::Accuracy::SCREEN) return false;
431
432 // Calculate what display lines are scanned between current
433 // renderer time and update-to time.
434 // Note: displayY1 is inclusive.
435 int deltaY = vdp.getVerticalScroll() - vdp.getLineZero();
436 int limitY = vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE;
437 int displayY0 = (nextY + deltaY) & 255;
438 int displayY1 = (limitY + deltaY) & 255;
439
440 switch(vdp.getDisplayMode().getBase()) {
443 if (vram.colorTable.isInside(offset)) {
444 unsigned vramQuarter = (offset & 0x1800) >> 11;
445 unsigned mask = (vram.colorTable.getMask() & 0x1800) >> 11;
446 for (auto i : xrange(4)) {
447 if ((i & mask) == vramQuarter
448 && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
449 /*fprintf(stderr,
450 "color table: %05X %04X - quarter %d\n",
451 offset, offset & 0x1FFF, i
452 );*/
453 return true;
454 }
455 }
456 }
457 if (vram.patternTable.isInside(offset)) {
458 unsigned vramQuarter = (offset & 0x1800) >> 11;
459 unsigned mask = (vram.patternTable.getMask() & 0x1800) >> 11;
460 for (auto i : xrange(4)) {
461 if ((i & mask) == vramQuarter
462 && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
463 /*fprintf(stderr,
464 "pattern table: %05X %04X - quarter %d\n",
465 offset, offset & 0x1FFF, i
466 );*/
467 return true;
468 }
469 }
470 }
471 if (vram.nameTable.isInside(offset)) {
472 int vramLine = narrow<int>(((offset & 0x3FF) / 32) * 8);
473 if (overlap(displayY0, displayY1, vramLine, vramLine + 8)) {
474 /*fprintf(stderr,
475 "name table: %05X %03X - line %d\n",
476 offset, offset & 0x3FF, vramLine
477 );*/
478 return true;
479 }
480 }
481 return false;
484 if (vdp.isFastBlinkEnabled()) {
485 // TODO could be improved
486 return true;
487 }
488 // Is the address inside the visual page(s)?
489 // TODO: Also look at which lines are touched inside pages.
490 unsigned visiblePage = vram.nameTable.getMask()
491 & (0x10000 | (vdp.getEvenOddMask() << 7));
492 if (vdp.isMultiPageScrolling()) {
493 return (offset & 0x18000) == visiblePage
494 || (offset & 0x18000) == (visiblePage & 0x10000);
495 } else {
496 return (offset & 0x18000) == visiblePage;
497 }
498 }
501 return true; // TODO: Implement better detection.
502 default:
503 // Range unknown; assume full range.
504 return vram.nameTable.isInside(offset)
505 || vram.colorTable.isInside(offset)
506 || vram.patternTable.isInside(offset);
507 }
508}
509
510void PixelRenderer::updateVRAM(unsigned offset, EmuTime::param time)
511{
512 // Note: No need to sync if display is disabled, because then the
513 // output does not depend on VRAM (only on background color).
514 if (renderFrame && displayEnabled && checkSync(offset, time)) {
515 renderUntil(time);
516 }
517}
518
519void PixelRenderer::updateWindow(bool /*enabled*/, EmuTime::param /*time*/)
520{
521 // The bitmapVisibleWindow has moved to a different area.
522 // This update is redundant: Renderer will be notified in another way
523 // as well (updateDisplayEnabled or updateNameBase, for example).
524 // TODO: Can this be used as the main update method instead?
525}
526
527void PixelRenderer::sync(EmuTime::param time, bool force)
528{
529 if (!renderFrame) return;
530
531 // Synchronisation is done in two phases:
532 // 1. update VRAM
533 // 2. update other subsystems
534 // Note that as part of step 1, type 2 updates can be triggered.
535 // Executing step 2 takes care of the subsystem changes that occur
536 // after the last VRAM update.
537 // This scheme makes sure type 2 routines such as renderUntil and
538 // checkUntil are not re-entered, which was causing major pain in
539 // the past.
540 // TODO: I wonder if it's possible to enforce this synchronisation
541 // scheme at a higher level. Probably. But how...
542 //if ((frameSkipCounter == 0) && TODO
543 if (accuracy != RenderSettings::Accuracy::SCREEN || force) {
544 vram.sync(time);
545 renderUntil(time);
546 }
547}
548
549void PixelRenderer::renderUntil(EmuTime::param time)
550{
551 // Translate from time to pixel position.
552 int limitTicks = vdp.getTicksThisFrame(time);
553 assert(limitTicks <= vdp.getTicksPerFrame());
554 auto [limitX, limitY] = [&]() -> std::pair<int, int> {
555 switch (accuracy) {
557 return {limitTicks % VDP::TICKS_PER_LINE,
558 limitTicks / VDP::TICKS_PER_LINE};
559 }
562 // Note: I'm not sure the rounding point is optimal.
563 // It used to be based on the left margin, but that doesn't work
564 // because the margin can change which leads to a line being
565 // rendered even though the time doesn't advance.
566 return {0,
567 (limitTicks + VDP::TICKS_PER_LINE - 400) / VDP::TICKS_PER_LINE};
568 default:
570 }
571 }();
572
573 // Stop here if there is nothing to render.
574 // This ensures that no pixels are rendered in a series of updates that
575 // happen at exactly the same time; the VDP subsystem states may be
576 // inconsistent until all updates are performed.
577 // Also it is a small performance optimisation.
578 if (limitX == nextX && limitY == nextY) return;
579
580 if (displayEnabled) {
581 if (vdp.spritesEnabled()) {
582 // Update sprite checking, so that rasterizer can call getSprites.
583 spriteChecker.checkUntil(time);
584 }
585
586 // Calculate start and end of borders in ticks since start of line.
587 // The 0..7 extra horizontal scroll low pixels should be drawn in
588 // border color. These will be drawn together with the border,
589 // but sprites above these pixels are clipped at the actual border
590 // rather than the end of the border colored area.
591 // TODO: Move these calculations and getDisplayLeft() to VDP.
592 int borderL = vdp.getLeftBorder();
593 int displayL =
594 vdp.isBorderMasked() ? borderL : vdp.getLeftBackground();
595 int borderR = vdp.getRightBorder();
596
597 // It's important that right border is drawn last (after left
598 // border and display area). See comment in SDLRasterizer::drawBorder().
599 // Left border.
600 subdivide(nextX, nextY, limitX, limitY,
601 0, displayL, DRAW_BORDER);
602 // Display area.
603 subdivide(nextX, nextY, limitX, limitY,
604 displayL, borderR, DRAW_DISPLAY);
605 // Right border.
606 subdivide(nextX, nextY, limitX, limitY,
607 borderR, VDP::TICKS_PER_LINE, DRAW_BORDER);
608 } else {
609 subdivide(nextX, nextY, limitX, limitY,
610 0, VDP::TICKS_PER_LINE, DRAW_BORDER);
611 }
612
613 nextX = limitX;
614 nextY = limitY;
615}
616
617void PixelRenderer::update(const Setting& setting) noexcept
618{
619 assert(&setting == one_of(&renderSettings.getMinFrameSkipSetting(),
620 &renderSettings.getMaxFrameSkipSetting()));
621 (void)setting;
622 // Force drawing of frame.
623 frameSkipCounter = 999;
624}
625
626} // namespace openmsx
BaseSetting * setting
Represents a VDP display mode.
constexpr byte getBase() const
Get the base display mode as an integer: M5..M1 combined.
static constexpr uint8_t GRAPHIC3
static constexpr uint8_t GRAPHIC4
static constexpr uint8_t GRAPHIC5
static constexpr uint8_t GRAPHIC7
static constexpr uint8_t GRAPHIC6
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
static constexpr uint8_t GRAPHIC2
constexpr byte getByte() const
Get the display mode as a byte: YAE YJK M5..M1 combined.
Represents the output window/screen of openMSX.
Definition Display.hh:32
void distributeEvent(Event &&event)
Schedule the given event for delivery.
This event is send when a device (v99x8, v9990, video9000, laserdisc) reaches the end of a frame.
Definition Event.hh:314
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition MSXDevice.cc:70
void updateSuperimposing(const RawFrame *videoSource, EmuTime::param time) override
Informs the renderer of a VDP superimposing change.
void updateWindow(bool enabled, EmuTime::param time) override
Informs the observer that the entire VRAM window will change.
PostProcessor * getPostProcessor() const override
See VDP::getPostProcessor.
void updateColorBase(unsigned addr, EmuTime::param time) override
Informs the renderer of a color table base address change.
void updateBorderMask(bool masked, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the border mask has been enabled/disabled.
void updateVerticalScroll(int scroll, EmuTime::param time) override
Informs the renderer of a vertical scroll change.
void updateBlinkState(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP blinking state change.
void updateTransparency(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP transparency enable/disable change.
void updateDisplayMode(DisplayMode mode, EmuTime::param time) override
Informs the renderer of a VDP display mode change.
void updateHorizontalAdjust(int adjust, EmuTime::param time) override
Informs the renderer of a horizontal adjust change.
PixelRenderer(VDP &vdp, Display &display)
void updateVRAM(unsigned offset, EmuTime::param time) override
Informs the observer of a change in VRAM contents.
void frameEnd(EmuTime::param time) override
Signals the end of a frame.
void updateSpritesEnabled(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP sprites enabled change.
void updateHorizontalScrollHigh(byte scroll, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the higher scroll value has changed.
void frameStart(EmuTime::param time) override
Signals the start of a new frame.
void updateBackgroundColor(byte color, EmuTime::param time) override
Informs the renderer of a VDP background color change.
void updateHorizontalScrollLow(byte scroll, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the lower scroll value has changed.
void updatePatternBase(unsigned addr, EmuTime::param time) override
Informs the renderer of a pattern table base address change.
void updateMultiPage(bool multiPage, EmuTime::param time) override
Informs the renderer of a horizontal scroll change: the multi page setting has changed.
void reInit() override
Reinitialize Renderer state.
void updateNameBase(unsigned addr, EmuTime::param time) override
Informs the renderer of a name table base address change.
void updatePalette(unsigned index, int grb, EmuTime::param time) override
Informs the renderer of a VDP palette change.
void updateBlinkBackgroundColor(byte color, EmuTime::param time) override
Informs the renderer of a VDP blink background color change.
void updateForegroundColor(byte color, EmuTime::param time) override
Informs the renderer of a VDP foreground color change.
void updateDisplayEnabled(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP display enabled change.
void updateBlinkForegroundColor(byte color, EmuTime::param time) override
Informs the renderer of a VDP blink foreground color change.
A post processor builds the frame that is displayed from the MSX frame, while applying effects such a...
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition RawFrame.hh:16
bool timeLeft(uint64_t us, EmuTime::param time) const
Check that there is enough real time left before we reach as certain point in emulated time.
Definition RealTime.cc:67
IntegerSetting & getMinFrameSkipSetting()
The current min frameskip.
Accuracy getAccuracy() const
Accuracy [screen, line, pixel].
IntegerSetting & getMaxFrameSkipSetting()
The current max frameskip.
double getSpeed() const
Return the desired ratio between EmuTime and real time.
void checkUntil(EmuTime::param time)
Update sprite checking until specified line.
void detach(Observer< T > &observer)
Definition Subject.hh:60
void attach(Observer< T > &observer)
Definition Subject.hh:54
bool isThrottled() const
Ask if throttling is enabled.
VRAMWindow colorTable
Definition VDPVRAM.hh:690
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition VDPVRAM.hh:416
VRAMWindow patternTable
Definition VDPVRAM.hh:691
VRAMWindow nameTable
Definition VDPVRAM.hh:689
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:66
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition VDP.hh:438
unsigned getEvenOddMask() const
Expresses the state of even/odd page interchange in a mask on the line number.
Definition VDP.hh:453
bool spritesEnabled() const
Are sprites enabled?
Definition VDP.hh:306
int getLeftBackground() const
Gets the number of VDP clock ticks between start of line and the time when the background pixel with ...
Definition VDP.hh:625
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition VDP.hh:430
bool isFastBlinkEnabled() const
Get 'fast-blink' status.
Definition VDP.hh:415
bool isBorderMasked() const
Gets the current border mask setting.
Definition VDP.hh:357
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
Definition VDP.hh:75
int getTicksPerFrame() const
Gets the number of VDP clock ticks (21MHz) per frame.
Definition VDP.hh:565
int getRightBorder() const
Gets the number of VDP clock ticks between start of line and the start of the right border.
Definition VDP.hh:615
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
Definition VDP.hh:155
int getLineZero() const
Get the absolute line number of display line zero.
Definition VDP.hh:382
byte getBackgroundColor() const
Gets the current background color.
Definition VDP.hh:218
byte getVerticalScroll() const
Gets the current vertical scroll (line displayed at Y=0).
Definition VDP.hh:330
bool isMultiPageScrolling() const
Is multi page scrolling enabled? It is considered enabled if both the multi page scrolling flag is en...
Definition VDP.hh:367
int getLeftBorder() const
Gets the number of VDP clock ticks between start of line and the end of the left border.
Definition VDP.hh:608
bool isInterlaced() const
Get interlace status.
Definition VDP.hh:401
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
Definition VDP.hh:298
int getTicksThisFrame(EmuTime::param time) const
Gets the number of VDP clock ticks (21MHz) elapsed between a given time and the start of this frame.
Definition VDP.hh:523
byte getHorizontalScrollLow() const
Gets the current horizontal scroll lower bits.
Definition VDP.hh:339
int getLeftSprites() const
Gets the number of VDP clock ticks between start of line and the start of the sprite plane.
Definition VDP.hh:597
bool isInside(unsigned address) const
Test whether an address is inside this window.
Definition VDPVRAM.hh:308
unsigned getMask() const
Gets the mask for this window.
Definition VDPVRAM.hh:147
uint64_t getTime()
Get current (real) time in us.
Definition Timer.cc:7
This file implemented 3 utility functions:
Definition Autofire.cc:11
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132