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  // Is the address inside the visual page(s)?
469  // TODO: Also look at which lines are touched inside pages.
470  int visiblePage = vram.nameTable.getMask()
471  & (0x10000 | (vdp.getEvenOddMask() << 7));
472  if (vdp.isMultiPageScrolling()) {
473  return (offset & 0x18000) == visiblePage
474  || (offset & 0x18000) == (visiblePage & 0x10000);
475  } else {
476  return (offset & 0x18000) == visiblePage;
477  }
478  }
481  return true; // TODO: Implement better detection.
482  default:
483  // Range unknown; assume full range.
484  return vram.nameTable.isInside(offset)
485  || vram.colorTable.isInside(offset)
486  || vram.patternTable.isInside(offset);
487  }
488 }
489 
490 void PixelRenderer::updateVRAM(unsigned offset, EmuTime::param time)
491 {
492  // Note: No need to sync if display is disabled, because then the
493  // output does not depend on VRAM (only on background color).
494  if (renderFrame && displayEnabled && checkSync(offset, time)) {
495  //fprintf(stderr, "vram sync @ line %d\n",
496  // vdp.getTicksThisFrame(time) / VDP::TICKS_PER_LINE);
497  renderUntil(time);
498  }
499 }
500 
501 void PixelRenderer::updateWindow(bool /*enabled*/, EmuTime::param /*time*/)
502 {
503  // The bitmapVisibleWindow has moved to a different area.
504  // This update is redundant: Renderer will be notified in another way
505  // as well (updateDisplayEnabled or updateNameBase, for example).
506  // TODO: Can this be used as the main update method instead?
507 }
508 
509 void PixelRenderer::sync(EmuTime::param time, bool force)
510 {
511  if (!renderFrame) return;
512 
513  // Synchronisation is done in two phases:
514  // 1. update VRAM
515  // 2. update other subsystems
516  // Note that as part of step 1, type 2 updates can be triggered.
517  // Executing step 2 takes care of the subsystem changes that occur
518  // after the last VRAM update.
519  // This scheme makes sure type 2 routines such as renderUntil and
520  // checkUntil are not re-entered, which was causing major pain in
521  // the past.
522  // TODO: I wonder if it's possible to enforce this synchronisation
523  // scheme at a higher level. Probably. But how...
524  //if ((frameSkipCounter == 0) && TODO
525  if (accuracy != RenderSettings::ACC_SCREEN || force) {
526  vram.sync(time);
527  renderUntil(time);
528  }
529 }
530 
531 void PixelRenderer::renderUntil(EmuTime::param time)
532 {
533  // Translate from time to pixel position.
534  int limitTicks = vdp.getTicksThisFrame(time);
535  assert(limitTicks <= vdp.getTicksPerFrame());
536  int limitX, limitY;
537  switch (accuracy) {
539  limitX = limitTicks % VDP::TICKS_PER_LINE;
540  limitY = limitTicks / VDP::TICKS_PER_LINE;
541  break;
542  }
545  // Note: I'm not sure the rounding point is optimal.
546  // It used to be based on the left margin, but that doesn't work
547  // because the margin can change which leads to a line being
548  // rendered even though the time doesn't advance.
549  limitX = 0;
550  limitY =
551  (limitTicks + VDP::TICKS_PER_LINE - 400) / VDP::TICKS_PER_LINE;
552  break;
553  }
554  default:
555  UNREACHABLE;
556  limitX = limitY = 0; // avoid warning
557  }
558 
559  // Stop here if there is nothing to render.
560  // This ensures that no pixels are rendered in a series of updates that
561  // happen at exactly the same time; the VDP subsystem states may be
562  // inconsistent until all updates are performed.
563  // Also it is a small performance optimisation.
564  if (limitX == nextX && limitY == nextY) return;
565 
566  if (displayEnabled) {
567  if (vdp.spritesEnabled()) {
568  // Update sprite checking, so that rasterizer can call getSprites.
569  spriteChecker.checkUntil(time);
570  }
571 
572  // Calculate start and end of borders in ticks since start of line.
573  // The 0..7 extra horizontal scroll low pixels should be drawn in
574  // border color. These will be drawn together with the border,
575  // but sprites above these pixels are clipped at the actual border
576  // rather than the end of the border colored area.
577  // TODO: Move these calculations and getDisplayLeft() to VDP.
578  int borderL = vdp.getLeftBorder();
579  int displayL =
580  vdp.isBorderMasked() ? borderL : vdp.getLeftBackground();
581  int borderR = vdp.getRightBorder();
582 
583  // It's important that right border is drawn last (after left
584  // border and display area). See comment in SDLRasterizer::drawBorder().
585  // Left border.
586  subdivide(nextX, nextY, limitX, limitY,
587  0, displayL, DRAW_BORDER);
588  // Display area.
589  subdivide(nextX, nextY, limitX, limitY,
590  displayL, borderR, DRAW_DISPLAY);
591  // Right border.
592  subdivide(nextX, nextY, limitX, limitY,
593  borderR, VDP::TICKS_PER_LINE, DRAW_BORDER);
594  } else {
595  subdivide(nextX, nextY, limitX, limitY,
596  0, VDP::TICKS_PER_LINE, DRAW_BORDER);
597  }
598 
599  nextX = limitX;
600  nextY = limitY;
601 }
602 
603 void PixelRenderer::update(const Setting& setting)
604 {
605  if (&setting == &renderSettings.getMinFrameSkipSetting() ||
606  &setting == &renderSettings.getMaxFrameSkipSetting()) {
607  // Force drawing of frame.
608  frameSkipCounter = 999;
609  } else {
610  UNREACHABLE;
611  }
612 }
613 
614 } // 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:355
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:450
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:369
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:348
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:45
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:467
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:407
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:439
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.
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:51
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:378
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:457
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.