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