openMSX
SpriteChecker.hh
Go to the documentation of this file.
1 #ifndef SPRITECHECKER_HH
2 #define SPRITECHECKER_HH
3 
4 #include "VDP.hh"
5 #include "VDPVRAM.hh"
6 #include "VRAMObserver.hh"
7 #include "DisplayMode.hh"
8 #include "serialize_meta.hh"
9 #include "ranges.hh"
10 #include "unreachable.hh"
11 #include <cstdint>
12 
13 namespace openmsx {
14 
15 class RenderSettings;
16 class BooleanSetting;
17 
18 class SpriteChecker final : public VRAMObserver
19 {
20 public:
26  using SpritePattern = uint32_t;
27 
30  struct SpriteInfo {
36  int16_t x;
43  };
44 
50  SpriteChecker(VDP& vdp, RenderSettings& renderSettings,
51  EmuTime::param time);
52 
56  void reset(EmuTime::param time);
57 
62  inline void sync(EmuTime::param time) {
63  if (!updateSpritesMethod) {
64  // Optimization: skip vram sync and sprite checks
65  // in sprite mode 0.
66  return;
67  }
68  // Debug:
69  // This method is not re-entrant, so check explicitly that it is not
70  // re-entered. This can disappear once the VDP-internal scheduling
71  // has become stable.
72  #ifdef DEBUG
73  static bool syncInProgress = false;
74  assert(!syncInProgress);
75  syncInProgress = true;
76  #endif
77  vram.sync(time);
78  checkUntil(time);
79  #ifdef DEBUG
80  syncInProgress = false;
81  #endif
82  }
83 
86  inline void resetStatus() {
87  // TODO: Used to be 0x5F, but that is contradicted by
88  // TMS9918.pdf. Check on real MSX.
89  vdp.setSpriteStatus(vdp.getStatusReg0() & 0x1F);
90  }
91 
96  inline void updateDisplayMode(DisplayMode mode, EmuTime::param time) {
97  sync(time);
98  setDisplayMode(mode);
99 
100  // The following is only required when switching from sprite
101  // mode0 to some other mode (in other case it has no effect).
102  // Because in mode 0, currentLine is not updated.
103  currentLine = frameStartTime.getTicksTill_fast(time)
105  // Every line in mode0 has 0 sprites, but none of the lines
106  // are ever requested by the renderer, except for the last
107  // line, because sprites are checked one line before they
108  // are displayed. Though frameStart() already makes sure
109  // spriteCount contains zero for all lines.
110  // spriteCount[currentLine - 1] = 0;
111  }
112 
117  inline void updateDisplayEnabled(bool enabled, EmuTime::param time) {
118  (void)enabled;
119  sync(time);
120  // TODO: Speed up sprite checking in display disabled case.
121  }
122 
127  inline void updateSpritesEnabled(bool enabled, EmuTime::param time) {
128  (void)enabled;
129  sync(time);
130  // TODO: Speed up sprite checking in display disabled case.
131  }
132 
139  inline void updateSpriteSizeMag(byte sizeMag, EmuTime::param time) {
140  (void)sizeMag;
141  sync(time);
142  // TODO: Precalc something?
143  }
144 
149  inline void updateTransparency(bool tp, EmuTime::param time) {
150  (void)tp;
151  sync(time);
152  }
153 
158  inline void updateVerticalScroll(int scroll, EmuTime::param time) {
159  (void)scroll;
160  sync(time);
161  // TODO: Precalc something?
162  }
163 
169  inline void checkUntil(EmuTime::param time) {
170  // TODO:
171  // Currently the sprite checking is done atomically at the end of
172  // the display line. In reality, sprite checking is probably done
173  // during most of the line. Run tests on real MSX to make a more
174  // accurate model of sprite checking.
175  int limit = frameStartTime.getTicksTill_fast(time)
177  if (currentLine < limit) {
178  // Call the right update method for the current display mode.
179  (this->*updateSpritesMethod)(limit);
180  }
181  }
182 
185  inline int getCollisionX(EmuTime::param time) {
186  sync(time);
187  return collisionX;
188  }
189 
192  inline int getCollisionY(EmuTime::param time) {
193  sync(time);
194  return collisionY;
195  }
196 
201  inline void resetCollision() {
202  collisionX = collisionY = 0;
203  }
204 
208  inline void frameStart(EmuTime::param time) {
209  frameStartTime.reset(time);
210  currentLine = 0;
211  ranges::fill(spriteCount, 0);
212  // TODO: Reset anything else? Does the real VDP?
213  }
214 
218  inline void frameEnd(EmuTime::param time) {
219  sync(time);
220  }
221 
235  inline int getSprites(int line, const SpriteInfo*& visibleSprites) const {
236  // Compensate for the fact sprites are checked one line earlier
237  // than they are displayed.
238  line--;
239 
240  // TODO: Is there ever a sprite on absolute line 0?
241  // Maybe there is, but it is never displayed.
242  if (line < 0) return 0;
243 
244  visibleSprites = spriteBuffer[line];
245  return spriteCount[line];
246  }
247 
248  // VRAMObserver implementation:
249 
250  void updateVRAM(unsigned /*offset*/, EmuTime::param time) override {
251  checkUntil(time);
252  }
253 
254  void updateWindow(bool /*enabled*/, EmuTime::param time) override {
255  sync(time);
256  }
257 
258  template<typename Archive>
259  void serialize(Archive& ar, unsigned version);
260 
261 private:
264  inline void setDisplayMode(DisplayMode mode) {
265  switch (mode.getSpriteMode(vdp.isMSX1VDP())) {
266  case 0:
267  updateSpritesMethod = nullptr;
268  break;
269  case 1:
270  updateSpritesMethod = &SpriteChecker::updateSprites1;
271  break;
272  case 2:
273  updateSpritesMethod = &SpriteChecker::updateSprites2;
274  planar = mode.isPlanar();
275  // An alternative is to have a planar and non-planar
276  // updateSprites2 method.
277  break;
278  default:
279  UNREACHABLE;
280  }
281  }
282 
285  void updateSprites1(int limit);
286 
289  void updateSprites2(int limit);
290 
299  inline SpritePattern calculatePatternNP(unsigned patternNr, unsigned y);
300  inline SpritePattern calculatePatternPlanar(unsigned patternNr, unsigned y);
301 
312  inline void checkSprites1(int minLine, int maxLine);
313 
324  inline void checkSprites2(int minLine, int maxLine);
325 
326  using UpdateSpritesMethod = void (SpriteChecker::*)(int limit);
327  UpdateSpritesMethod updateSpritesMethod;
328 
331  VDP& vdp;
332 
335  VDPVRAM& vram;
336 
343  BooleanSetting& limitSpritesSetting;
344 
347  Clock<VDP::TICKS_PER_SECOND> frameStartTime;
348 
351  int currentLine;
352 
356  int collisionX;
357 
363  int collisionY;
364 
368  SpriteInfo spriteBuffer[313][32 + 1]; // +1 for sentinel
369 
375  uint8_t spriteCount[313];
376 
380  bool planar;
381 };
383 
384 } // namespace openmsx
385 
386 #endif
openmsx::SpriteChecker::updateDisplayMode
void updateDisplayMode(DisplayMode mode, EmuTime::param time)
Informs the sprite checker of a VDP display mode change.
Definition: SpriteChecker.hh:96
openmsx::SpriteChecker::resetCollision
void resetCollision()
Reset sprite collision coordinates.
Definition: SpriteChecker.hh:201
openmsx::VDPVRAM
VDPVRAM
Definition: VDPVRAM.cc:343
openmsx::VDP
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:61
openmsx::RenderSettings
Class containing all settings for renderers.
Definition: RenderSettings.hh:21
openmsx::SpriteChecker::updateSpritesEnabled
void updateSpritesEnabled(bool enabled, EmuTime::param time)
Informs the sprite checker of sprite enable changes.
Definition: SpriteChecker.hh:127
serialize_meta.hh
openmsx::SpriteChecker::checkUntil
void checkUntil(EmuTime::param time)
Update sprite checking until specified line.
Definition: SpriteChecker.hh:169
VDP.hh
openmsx::SpriteChecker
SpriteChecker
Definition: SpriteChecker.cc:524
openmsx::DisplayMode::isPlanar
constexpr bool isPlanar() const
Is VRAM "planar" in the current display mode? Graphic 6 and 7 spread their bytes over two VRAM ICs,...
Definition: DisplayMode.hh:148
ranges.hh
openmsx::SpriteChecker::updateWindow
void updateWindow(bool, EmuTime::param time) override
Informs the observer that the entire VRAM window will change.
Definition: SpriteChecker.hh:254
openmsx::VDP::setSpriteStatus
void setSpriteStatus(byte value)
Should only be used by SpriteChecker.
Definition: VDP.hh:574
openmsx::SpriteChecker::resetStatus
void resetStatus()
Clear status bits triggered by reading of S#0.
Definition: SpriteChecker.hh:86
openmsx::SpriteChecker::SpriteChecker
SpriteChecker(VDP &vdp, RenderSettings &renderSettings, EmuTime::param time)
Create a sprite checker.
Definition: SpriteChecker.cc:20
openmsx::DisplayMode
Represents a VDP display mode.
Definition: DisplayMode.hh:15
openmsx::VRAMObserver
Interface that can be registered at VRAMWindow, to be called when the contents of the VRAM inside tha...
Definition: VRAMObserver.hh:11
DisplayMode.hh
openmsx::SERIALIZE_CLASS_VERSION
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
openmsx::SpriteChecker::SpriteInfo::x
int16_t x
X-coordinate of sprite, corrected for early clock.
Definition: SpriteChecker.hh:36
openmsx::SpriteChecker::reset
void reset(EmuTime::param time)
Puts the sprite checker in its initial state.
Definition: SpriteChecker.cc:30
UNREACHABLE
#define UNREACHABLE
Definition: unreachable.hh:38
openmsx::SpriteChecker::serialize
void serialize(Archive &ar, unsigned version)
Definition: SpriteChecker.cc:499
openmsx::SpriteChecker::updateVerticalScroll
void updateVerticalScroll(int scroll, EmuTime::param time)
Informs the sprite checker of a vertical scroll change.
Definition: SpriteChecker.hh:158
openmsx::SpriteChecker
Definition: SpriteChecker.hh:18
openmsx::SpriteChecker::SpriteInfo::colorAttrib
byte colorAttrib
Bit 3..0 are index in palette.
Definition: SpriteChecker.hh:42
openmsx::VDP::isMSX1VDP
bool isMSX1VDP() const
Is this an MSX1 VDP?
Definition: VDP.hh:93
openmsx::VDPVRAM::sync
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition: VDPVRAM.hh:400
openmsx::SpriteChecker::getSprites
int getSprites(int line, const SpriteInfo *&visibleSprites) const
Get sprites for a display line.
Definition: SpriteChecker.hh:235
openmsx::SpriteChecker::updateTransparency
void updateTransparency(bool tp, EmuTime::param time)
Informs the sprite checker of a change in the TP bit (R#8 bit 5)
Definition: SpriteChecker.hh:149
openmsx::SpriteChecker::updateVRAM
void updateVRAM(unsigned, EmuTime::param time) override
Informs the observer of a change in VRAM contents.
Definition: SpriteChecker.hh:250
openmsx::SpriteChecker::SpriteInfo::pattern
SpritePattern pattern
Pattern of this sprite line, corrected for magnification.
Definition: SpriteChecker.hh:33
openmsx::Clock::reset
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Definition: Clock.hh:102
openmsx::SpriteChecker::frameStart
void frameStart(EmuTime::param time)
Signals the start of a new frame.
Definition: SpriteChecker.hh:208
openmsx::VDP
VDP
Definition: VDP.cc:1917
openmsx::SpriteChecker::SpriteInfo
Contains all the information to draw a line of a sprite.
Definition: SpriteChecker.hh:30
openmsx::SpriteChecker::updateDisplayEnabled
void updateDisplayEnabled(bool enabled, EmuTime::param time)
Informs the sprite checker of a VDP display enabled change.
Definition: SpriteChecker.hh:117
openmsx::VDP::getStatusReg0
byte getStatusReg0() const
Should only be used by SpriteChecker.
Definition: VDP.hh:564
VDPVRAM.hh
openmsx::SpriteChecker::getCollisionY
int getCollisionY(EmuTime::param time)
Get Y coordinate of sprite collision.
Definition: SpriteChecker.hh:192
openmsx::SpriteChecker::updateSpriteSizeMag
void updateSpriteSizeMag(byte sizeMag, EmuTime::param time)
Informs the sprite checker of sprite size or magnification changes.
Definition: SpriteChecker.hh:139
openmsx::Clock::getTicksTill_fast
constexpr unsigned getTicksTill_fast(EmuTime::param e) const
Same as above, only faster, Though the time interval may not be too large.
Definition: Clock.hh:70
openmsx::DisplayMode::getSpriteMode
constexpr int getSpriteMode(bool isMSX1) const
Get the sprite mode of this display mode.
Definition: DisplayMode.hh:166
openmsx::SpriteChecker::frameEnd
void frameEnd(EmuTime::param time)
Signals the end of the current frame.
Definition: SpriteChecker.hh:218
openmsx::SpriteChecker::SpritePattern
uint32_t SpritePattern
Bitmap of length 32 describing a sprite pattern.
Definition: SpriteChecker.hh:26
openmsx::SpriteChecker::sync
void sync(EmuTime::param time)
Update sprite checking to specified time.
Definition: SpriteChecker.hh:62
unreachable.hh
ranges::fill
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
openmsx::VDP::TICKS_PER_LINE
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
Definition: VDP.hh:72
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
VRAMObserver.hh
openmsx::SpriteChecker::getCollisionX
int getCollisionX(EmuTime::param time)
Get X coordinate of sprite collision.
Definition: SpriteChecker.hh:185