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(
247 Event::create<FinishFrameEvent>(
248 rasterizer->getPostProcessor()->getVideoSource(),
249 videoSourceSetting.getSource(),
250 !paintFrame));
251 }
252}
253
255 byte scroll, EmuTime::param time)
256{
257 if (displayEnabled) sync(time);
258 rasterizer->setHorizontalScrollLow(scroll);
259}
260
262 byte /*scroll*/, EmuTime::param time)
263{
264 if (displayEnabled) sync(time);
265}
266
268 bool masked, EmuTime::param time)
269{
270 if (displayEnabled) sync(time);
271 rasterizer->setBorderMask(masked);
272}
273
275 bool /*multiPage*/, EmuTime::param time)
276{
277 if (displayEnabled) sync(time);
278}
279
281 bool enabled, EmuTime::param time)
282{
283 if (displayEnabled) sync(time);
284 rasterizer->setTransparency(enabled);
285}
286
288 const RawFrame* videoSource, EmuTime::param time)
289{
290 if (displayEnabled) sync(time);
291 rasterizer->setSuperimposeVideoFrame(videoSource);
292}
293
295 int /*color*/, EmuTime::param time)
296{
297 if (displayEnabled) sync(time);
298}
299
301 int color, EmuTime::param time)
302{
303 sync(time);
304 rasterizer->setBackgroundColor(color);
305}
306
308 int /*color*/, EmuTime::param time)
309{
310 if (displayEnabled) sync(time);
311}
312
314 int /*color*/, EmuTime::param time)
315{
316 if (displayEnabled) sync(time);
317}
318
320 bool /*enabled*/, EmuTime::param /*time*/)
321{
322 // TODO: When the sync call is enabled, the screen flashes on
323 // every call to this method.
324 // I don't know why exactly, but it's probably related to
325 // being called at frame start.
326 //sync(time);
327}
328
330 unsigned index, int grb, EmuTime::param time)
331{
332 if (displayEnabled) {
333 sync(time);
334 } else {
335 // Only sync if border color changed.
336 DisplayMode mode = vdp.getDisplayMode();
337 if (mode.getBase() == DisplayMode::GRAPHIC5) {
338 auto bgColor = vdp.getBackgroundColor();
339 if (index == one_of(bgColor & 3, bgColor >> 2)) {
340 sync(time);
341 }
342 } else if (mode.getByte() != DisplayMode::GRAPHIC7) {
343 if (index == vdp.getBackgroundColor()) {
344 sync(time);
345 }
346 }
347 }
348 rasterizer->setPalette(index, grb);
349}
350
352 int /*scroll*/, EmuTime::param time)
353{
354 if (displayEnabled) sync(time);
355}
356
358 int adjust, EmuTime::param time)
359{
360 if (displayEnabled) sync(time);
361 rasterizer->setHorizontalAdjust(adjust);
362}
363
365 DisplayMode mode, EmuTime::param time)
366{
367 // Sync if in display area or if border drawing process changes.
368 DisplayMode oldMode = vdp.getDisplayMode();
369 if (displayEnabled
370 || oldMode.getByte() == DisplayMode::GRAPHIC5
371 || oldMode.getByte() == DisplayMode::GRAPHIC7
372 || mode.getByte() == DisplayMode::GRAPHIC5
373 || mode.getByte() == DisplayMode::GRAPHIC7) {
374 sync(time, true);
375 }
376 rasterizer->setDisplayMode(mode);
377}
378
380 unsigned /*addr*/, EmuTime::param time)
381{
382 if (displayEnabled) sync(time);
383}
384
386 unsigned /*addr*/, EmuTime::param time)
387{
388 if (displayEnabled) sync(time);
389}
390
392 unsigned /*addr*/, EmuTime::param time)
393{
394 if (displayEnabled) sync(time);
395}
396
398 bool /*enabled*/, EmuTime::param time
399) {
400 if (displayEnabled) sync(time);
401}
402
403static constexpr bool overlap(
404 int displayY0, // start of display region, inclusive
405 int displayY1, // end of display region, exclusive
406 int vramLine0, // start of VRAM region, inclusive
407 int vramLine1 // end of VRAM region, exclusive
408 // Note: Display region can wrap around: 256 -> 0.
409 // VRAM region cannot wrap around.
410) {
411 if (displayY0 <= displayY1) {
412 if (vramLine1 > displayY0) {
413 if (vramLine0 <= displayY1) return true;
414 }
415 } else {
416 if (vramLine1 > displayY0) return true;
417 if (vramLine0 <= displayY1) return true;
418 }
419 return false;
420}
421
422inline bool PixelRenderer::checkSync(unsigned offset, EmuTime::param time)
423{
424 // TODO: Because range is entire VRAM, offset == address.
425
426 // If display is disabled, VRAM changes will not affect the
427 // renderer output, therefore sync is not necessary.
428 // TODO: Have bitmapVisibleWindow disabled in this case.
429 if (!displayEnabled) return false;
430 //if (frameSkipCounter != 0) return false; // TODO
431 if (accuracy == RenderSettings::ACC_SCREEN) return false;
432
433 // Calculate what display lines are scanned between current
434 // renderer time and update-to time.
435 // Note: displayY1 is inclusive.
436 int deltaY = vdp.getVerticalScroll() - vdp.getLineZero();
437 int limitY = vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE;
438 int displayY0 = (nextY + deltaY) & 255;
439 int displayY1 = (limitY + deltaY) & 255;
440
441 switch(vdp.getDisplayMode().getBase()) {
444 if (vram.colorTable.isInside(offset)) {
445 unsigned vramQuarter = (offset & 0x1800) >> 11;
446 unsigned mask = (vram.colorTable.getMask() & 0x1800) >> 11;
447 for (auto i : xrange(4)) {
448 if ((i & mask) == vramQuarter
449 && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
450 /*fprintf(stderr,
451 "color table: %05X %04X - quarter %d\n",
452 offset, offset & 0x1FFF, i
453 );*/
454 return true;
455 }
456 }
457 }
458 if (vram.patternTable.isInside(offset)) {
459 unsigned vramQuarter = (offset & 0x1800) >> 11;
460 unsigned mask = (vram.patternTable.getMask() & 0x1800) >> 11;
461 for (auto i : xrange(4)) {
462 if ((i & mask) == vramQuarter
463 && overlap(displayY0, displayY1, i * 64, (i + 1) * 64)) {
464 /*fprintf(stderr,
465 "pattern table: %05X %04X - quarter %d\n",
466 offset, offset & 0x1FFF, i
467 );*/
468 return true;
469 }
470 }
471 }
472 if (vram.nameTable.isInside(offset)) {
473 int vramLine = narrow<int>(((offset & 0x3FF) / 32) * 8);
474 if (overlap(displayY0, displayY1, vramLine, vramLine + 8)) {
475 /*fprintf(stderr,
476 "name table: %05X %03X - line %d\n",
477 offset, offset & 0x3FF, vramLine
478 );*/
479 return true;
480 }
481 }
482 return false;
485 if (vdp.isFastBlinkEnabled()) {
486 // TODO could be improved
487 return true;
488 }
489 // Is the address inside the visual page(s)?
490 // TODO: Also look at which lines are touched inside pages.
491 unsigned visiblePage = vram.nameTable.getMask()
492 & (0x10000 | (vdp.getEvenOddMask() << 7));
493 if (vdp.isMultiPageScrolling()) {
494 return (offset & 0x18000) == visiblePage
495 || (offset & 0x18000) == (visiblePage & 0x10000);
496 } else {
497 return (offset & 0x18000) == visiblePage;
498 }
499 }
502 return true; // TODO: Implement better detection.
503 default:
504 // Range unknown; assume full range.
505 return vram.nameTable.isInside(offset)
506 || vram.colorTable.isInside(offset)
507 || vram.patternTable.isInside(offset);
508 }
509}
510
511void PixelRenderer::updateVRAM(unsigned offset, EmuTime::param time)
512{
513 // Note: No need to sync if display is disabled, because then the
514 // output does not depend on VRAM (only on background color).
515 if (renderFrame && displayEnabled && checkSync(offset, time)) {
516 //fprintf(stderr, "vram sync @ line %d\n",
517 // vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE);
518 renderUntil(time);
519 }
520}
521
522void PixelRenderer::updateWindow(bool /*enabled*/, EmuTime::param /*time*/)
523{
524 // The bitmapVisibleWindow has moved to a different area.
525 // This update is redundant: Renderer will be notified in another way
526 // as well (updateDisplayEnabled or updateNameBase, for example).
527 // TODO: Can this be used as the main update method instead?
528}
529
530void PixelRenderer::sync(EmuTime::param time, bool force)
531{
532 if (!renderFrame) return;
533
534 // Synchronisation is done in two phases:
535 // 1. update VRAM
536 // 2. update other subsystems
537 // Note that as part of step 1, type 2 updates can be triggered.
538 // Executing step 2 takes care of the subsystem changes that occur
539 // after the last VRAM update.
540 // This scheme makes sure type 2 routines such as renderUntil and
541 // checkUntil are not re-entered, which was causing major pain in
542 // the past.
543 // TODO: I wonder if it's possible to enforce this synchronisation
544 // scheme at a higher level. Probably. But how...
545 //if ((frameSkipCounter == 0) && TODO
546 if (accuracy != RenderSettings::ACC_SCREEN || force) {
547 vram.sync(time);
548 renderUntil(time);
549 }
550}
551
552void PixelRenderer::renderUntil(EmuTime::param time)
553{
554 // Translate from time to pixel position.
555 int limitTicks = vdp.getTicksThisFrame(time);
556 assert(limitTicks <= vdp.getTicksPerFrame());
557 auto [limitX, limitY] = [&]() -> std::pair<int, int> {
558 switch (accuracy) {
560 return {limitTicks % VDP::TICKS_PER_LINE,
561 limitTicks / VDP::TICKS_PER_LINE};
562 }
565 // Note: I'm not sure the rounding point is optimal.
566 // It used to be based on the left margin, but that doesn't work
567 // because the margin can change which leads to a line being
568 // rendered even though the time doesn't advance.
569 return {0,
570 (limitTicks + VDP::TICKS_PER_LINE - 400) / VDP::TICKS_PER_LINE};
571 default:
573 return {0, 0}; // avoid warning
574 }
575 }();
576
577 // Stop here if there is nothing to render.
578 // This ensures that no pixels are rendered in a series of updates that
579 // happen at exactly the same time; the VDP subsystem states may be
580 // inconsistent until all updates are performed.
581 // Also it is a small performance optimisation.
582 if (limitX == nextX && limitY == nextY) return;
583
584 if (displayEnabled) {
585 if (vdp.spritesEnabled()) {
586 // Update sprite checking, so that rasterizer can call getSprites.
587 spriteChecker.checkUntil(time);
588 }
589
590 // Calculate start and end of borders in ticks since start of line.
591 // The 0..7 extra horizontal scroll low pixels should be drawn in
592 // border color. These will be drawn together with the border,
593 // but sprites above these pixels are clipped at the actual border
594 // rather than the end of the border colored area.
595 // TODO: Move these calculations and getDisplayLeft() to VDP.
596 int borderL = vdp.getLeftBorder();
597 int displayL =
598 vdp.isBorderMasked() ? borderL : vdp.getLeftBackground();
599 int borderR = vdp.getRightBorder();
600
601 // It's important that right border is drawn last (after left
602 // border and display area). See comment in SDLRasterizer::drawBorder().
603 // Left border.
604 subdivide(nextX, nextY, limitX, limitY,
605 0, displayL, DRAW_BORDER);
606 // Display area.
607 subdivide(nextX, nextY, limitX, limitY,
608 displayL, borderR, DRAW_DISPLAY);
609 // Right border.
610 subdivide(nextX, nextY, limitX, limitY,
611 borderR, VDP::TICKS_PER_LINE, DRAW_BORDER);
612 } else {
613 subdivide(nextX, nextY, limitX, limitY,
614 0, VDP::TICKS_PER_LINE, DRAW_BORDER);
615 }
616
617 nextX = limitX;
618 nextY = limitY;
619}
620
621void PixelRenderer::update(const Setting& setting) noexcept
622{
623 assert(&setting == one_of(&renderSettings.getMinFrameSkipSetting(),
624 &renderSettings.getMaxFrameSkipSetting()));
625 (void)setting;
626 // Force drawing of frame.
627 frameSkipCounter = 999;
628}
629
630} // namespace openmsx
BaseSetting * setting
Definition: Interpreter.cc:28
Definition: one_of.hh:7
Represents a VDP display mode.
Definition: DisplayMode.hh:16
constexpr byte getBase() const
Get the base display mode as an integer: M5..M1 combined.
Definition: DisplayMode.hh:107
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
Definition: DisplayMode.hh:122
constexpr byte getByte() const
Get the display mode as a byte: YAE YJK M5..M1 combined.
Definition: DisplayMode.hh:93
Represents the output window/screen of openMSX.
Definition: Display.hh:33
void distributeEvent(Event &&event)
Schedule the given event for delivery.
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 updateForegroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP foreground color 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 updateBlinkBackgroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP blink 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 updateBackgroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP background color change.
void updateDisplayEnabled(bool enabled, EmuTime::param time) override
Informs the renderer of a VDP display enabled change.
void updateBlinkForegroundColor(int color, EmuTime::param time) override
Informs the renderer of a VDP blink foreground color change.
Abstract base class for post processors.
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
Definition: RawFrame.hh:15
bool timeLeft(uint64_t us, EmuTime::param time)
Check that there is enough real time left before we reach as certain point in emulated time.
Definition: RealTime.cc:64
bool getDeinterlace() const
Deinterlacing [on, off].
IntegerSetting & getMinFrameSkipSetting()
The current min frameskip.
Accuracy getAccuracy() const
Accuracy [screen, line, pixel].
bool getDisableSprites() const
Disable sprite rendering?
IntegerSetting & getMaxFrameSkipSetting()
The current max frameskip.
double getSpeed() const
Return the desired ratio between EmuTime and real time.
Definition: SpeedManager.hh:26
void checkUntil(EmuTime::param time)
Update sprite checking until specified line.
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
bool isThrottled() const
Ask if throttling is enabled.
VRAMWindow colorTable
Definition: VDPVRAM.hh:670
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition: VDPVRAM.hh:402
VRAMWindow patternTable
Definition: VDPVRAM.hh:671
VRAMWindow nameTable
Definition: VDPVRAM.hh:669
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:64
bool getEvenOdd() const
Is the even or odd field being displayed?
Definition: VDP.hh:384
unsigned getEvenOddMask() const
Expresses the state of even/odd page interchange in a mask on the line number.
Definition: VDP.hh:399
bool spritesEnabled() const
Are sprites enabled?
Definition: VDP.hh:259
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:564
bool isEvenOddEnabled() const
Get even/odd page alternation status.
Definition: VDP.hh:376
bool isFastBlinkEnabled() const
Get 'fast-blink' status.
Definition: VDP.hh:361
bool isBorderMasked() const
Gets the current border mask setting.
Definition: VDP.hh:310
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
Definition: VDP.hh:73
int getTicksPerFrame() const
Gets the number of VDP clock ticks (21MHz) per frame.
Definition: VDP.hh:504
int getRightBorder() const
Gets the number of VDP clock ticks between start of line and the start of the right border.
Definition: VDP.hh:554
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
Definition: VDP.hh:144
unsigned getBackgroundColor() const
Gets the current background color.
Definition: VDP.hh:207
int getLineZero() const
Get the absolute line number of display line zero.
Definition: VDP.hh:328
byte getVerticalScroll() const
Gets the current vertical scroll (line displayed at Y=0).
Definition: VDP.hh:283
bool isMultiPageScrolling() const
Is multi page scrolling enabled? It is considered enabled if both the multi page scrolling flag is en...
Definition: VDP.hh:320
int getLeftBorder() const
Gets the number of VDP clock ticks between start of line and the end of the left border.
Definition: VDP.hh:547
bool isInterlaced() const
Get interlace status.
Definition: VDP.hh:347
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
Definition: VDP.hh:251
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:469
byte getHorizontalScrollLow() const
Gets the current horizontal scroll lower bits.
Definition: VDP.hh:292
int getLeftSprites() const
Gets the number of VDP clock ticks between start of line and the start of the sprite plane.
Definition: VDP.hh:536
bool isInside(unsigned address) const
Test whether an address is inside this window.
Definition: VDPVRAM.hh:297
unsigned getMask() const
Gets the mask for this window.
Definition: VDPVRAM.hh:145
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:284
uint64_t getTime()
Get current (real) time in us.
Definition: Timer.cc:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:133