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 "narrow.hh"
9#include "ranges.hh"
10#include "serialize_meta.hh"
11#include "unreachable.hh"
12#include <array>
13#include <cstdint>
14#include <span>
15
16namespace openmsx {
17
18class RenderSettings;
19class BooleanSetting;
20
21class SpriteChecker final : public VRAMObserver
22{
23public:
29 using SpritePattern = uint32_t;
30
33 struct SpriteInfo {
39 int16_t x;
46 };
47
49 {
50 // bit-pattern "abcd...." gets expanded to "aabbccdd"
51 // upper 16 bits (of a 32 bit number) contain the pattern
52 // lower 16 bits must be zero
53 // // abcdefghijklmnop0000000000000000
54 a = (a | (a >> 8)) & 0xFF00FF00; // abcdefgh00000000ijklmnop00000000
55 a = (a | (a >> 4)) & 0xF0F0F0F0; // abcd0000efgh0000ijkl0000mnop0000
56 a = (a | (a >> 2)) & 0xCCCCCCCC; // ab00cd00ef00gh00ij00kl00mn00op00
57 a = (a | (a >> 1)) & 0xAAAAAAAA; // a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p0
58 return a | (a >> 1); // aabbccddeeffgghhiijjkkllmmnnoopp
59 }
60
66 SpriteChecker(VDP& vdp, RenderSettings& renderSettings,
67 EmuTime::param time);
68
72 void reset(EmuTime::param time);
73
78 inline void sync(EmuTime::param time) {
79 if (!updateSpritesMethod) {
80 // Optimization: skip vram sync and sprite checks
81 // in sprite mode 0.
82 return;
83 }
84 // Debug:
85 // This method is not re-entrant, so check explicitly that it is not
86 // re-entered. This can disappear once the VDP-internal scheduling
87 // has become stable.
88 #ifdef DEBUG
89 static bool syncInProgress = false;
90 assert(!syncInProgress);
91 syncInProgress = true;
92 #endif
93 vram.sync(time);
94 checkUntil(time);
95 #ifdef DEBUG
96 syncInProgress = false;
97 #endif
98 }
99
102 inline void resetStatus() {
103 // TODO: Used to be 0x5F, but that is contradicted by
104 // TMS9918.pdf. Check on real MSX.
105 vdp.setSpriteStatus(vdp.getStatusReg0() & 0x1F);
106 }
107
112 inline void updateDisplayMode(DisplayMode mode, EmuTime::param time) {
113 sync(time);
114 setDisplayMode(mode);
115
116 // The following is only required when switching from sprite
117 // mode0 to some other mode (in other case it has no effect).
118 // Because in mode 0, currentLine is not updated.
119 currentLine = narrow<int>(frameStartTime.getTicksTill_fast(time)
121 // Every line in mode0 has 0 sprites, but none of the lines
122 // are ever requested by the renderer, except for the last
123 // line, because sprites are checked one line before they
124 // are displayed. Though frameStart() already makes sure
125 // spriteCount contains zero for all lines.
126 // spriteCount[currentLine - 1] = 0;
127 }
128
133 inline void updateDisplayEnabled(bool enabled, EmuTime::param time) {
134 (void)enabled;
135 sync(time);
136 // TODO: Speed up sprite checking in display disabled case.
137 }
138
143 inline void updateSpritesEnabled(bool enabled, EmuTime::param time) {
144 (void)enabled;
145 sync(time);
146 // TODO: Speed up sprite checking in display disabled case.
147 }
148
155 inline void updateSpriteSizeMag(byte sizeMag, EmuTime::param time) {
156 (void)sizeMag;
157 sync(time);
158 // TODO: Precalc something?
159 }
160
165 inline void updateTransparency(bool tp, EmuTime::param time) {
166 (void)tp;
167 sync(time);
168 }
169
174 inline void updateVerticalScroll(int scroll, EmuTime::param time) {
175 (void)scroll;
176 sync(time);
177 // TODO: Precalc something?
178 }
179
185 inline void checkUntil(EmuTime::param time) {
186 // TODO:
187 // Currently the sprite checking is done atomically at the end of
188 // the display line. In reality, sprite checking is probably done
189 // during most of the line. Run tests on real MSX to make a more
190 // accurate model of sprite checking.
191 int limit = narrow<int>(frameStartTime.getTicksTill_fast(time)
193 if (currentLine < limit) {
194 // Call the right update method for the current display mode.
195 (this->*updateSpritesMethod)(limit);
196 }
197 }
198
201 [[nodiscard]] inline int getCollisionX(EmuTime::param time) {
202 sync(time);
203 return collisionX;
204 }
205
208 [[nodiscard]] inline int getCollisionY(EmuTime::param time) {
209 sync(time);
210 return collisionY;
211 }
212
217 inline void resetCollision() {
218 collisionX = collisionY = 0;
219 }
220
224 inline void frameStart(EmuTime::param time) {
225 frameStartTime.reset(time);
226 currentLine = 0;
227 ranges::fill(spriteCount, 0);
228 // TODO: Reset anything else? Does the real VDP?
229 }
230
234 inline void frameEnd(EmuTime::param time) {
235 sync(time);
236 }
237
251 [[nodiscard]] inline std::span<const SpriteInfo> getSprites(int line) const {
252 // Compensate for the fact sprites are checked one line earlier
253 // than they are displayed.
254 line--;
255
256 // TODO: Is there ever a sprite on absolute line 0?
257 // Maybe there is, but it is never displayed.
258 if (line < 0) return {};
259
260 return subspan(spriteBuffer[line], 0, spriteCount[line]);
261 }
262
263 // VRAMObserver implementation:
264
265 void updateVRAM(unsigned /*offset*/, EmuTime::param time) override {
266 checkUntil(time);
267 }
268
269 void updateWindow(bool /*enabled*/, EmuTime::param time) override {
270 sync(time);
271 }
272
273 template<typename Archive>
274 void serialize(Archive& ar, unsigned version);
275
276private:
279 inline void setDisplayMode(DisplayMode mode) {
280 switch (mode.getSpriteMode(vdp.isMSX1VDP())) {
281 case 0:
282 updateSpritesMethod = nullptr;
283 break;
284 case 1:
285 updateSpritesMethod = &SpriteChecker::updateSprites1;
286 break;
287 case 2:
288 updateSpritesMethod = &SpriteChecker::updateSprites2;
289 planar = mode.isPlanar();
290 // An alternative is to have a planar and non-planar
291 // updateSprites2 method.
292 break;
293 default:
295 }
296 }
297
300 void updateSprites1(int limit);
301
304 void updateSprites2(int limit);
305
314 [[nodiscard]] inline SpritePattern calculatePatternNP(unsigned patternNr, unsigned y) const;
315 [[nodiscard]] inline SpritePattern calculatePatternPlanar(unsigned patternNr, unsigned y) const;
316
327 inline void checkSprites1(int minLine, int maxLine);
328
339 inline void checkSprites2(int minLine, int maxLine);
340
341private:
342 using UpdateSpritesMethod = void (SpriteChecker::*)(int limit);
343 UpdateSpritesMethod updateSpritesMethod;
344
347 VDP& vdp;
348
351 VDPVRAM& vram;
352
359 BooleanSetting& limitSpritesSetting;
360
363 Clock<VDP::TICKS_PER_SECOND> frameStartTime;
364
367 int currentLine;
368
372 int collisionX;
373
379 int collisionY;
380
384 std::array<std::array<SpriteInfo, 32 + 1>, 313> spriteBuffer; // +1 for sentinel
385
391 std::array<uint8_t, 313> spriteCount;
392
396 bool planar;
397};
399
400} // namespace openmsx
401
402#endif
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Definition Clock.hh:102
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
Represents a VDP display mode.
constexpr bool isPlanar() const
Is VRAM "planar" in the current display mode? Graphic 6 and 7 spread their bytes over two VRAM ICs,...
constexpr int getSpriteMode(bool isMSX1) const
Get the sprite mode of this display mode.
Class containing all settings for renderers.
void frameStart(EmuTime::param time)
Signals the start of a new frame.
void sync(EmuTime::param time)
Update sprite checking to specified time.
void reset(EmuTime::param time)
Puts the sprite checker in its initial state.
void serialize(Archive &ar, unsigned version)
void frameEnd(EmuTime::param time)
Signals the end of the current frame.
uint32_t SpritePattern
Bitmap of length 32 describing a sprite pattern.
void updateTransparency(bool tp, EmuTime::param time)
Informs the sprite checker of a change in the TP bit (R#8 bit 5)
int getCollisionX(EmuTime::param time)
Get X coordinate of sprite collision.
void updateVerticalScroll(int scroll, EmuTime::param time)
Informs the sprite checker of a vertical scroll change.
void updateWindow(bool, EmuTime::param time) override
Informs the observer that the entire VRAM window will change.
void updateDisplayMode(DisplayMode mode, EmuTime::param time)
Informs the sprite checker of a VDP display mode change.
void resetStatus()
Clear status bits triggered by reading of S#0.
static constexpr SpritePattern doublePattern(SpritePattern a)
void updateDisplayEnabled(bool enabled, EmuTime::param time)
Informs the sprite checker of a VDP display enabled change.
void checkUntil(EmuTime::param time)
Update sprite checking until specified line.
void updateSpriteSizeMag(byte sizeMag, EmuTime::param time)
Informs the sprite checker of sprite size or magnification changes.
std::span< const SpriteInfo > getSprites(int line) const
Get sprites for a display line.
int getCollisionY(EmuTime::param time)
Get Y coordinate of sprite collision.
void resetCollision()
Reset sprite collision coordinates.
void updateVRAM(unsigned, EmuTime::param time) override
Informs the observer of a change in VRAM contents.
void updateSpritesEnabled(bool enabled, EmuTime::param time)
Informs the sprite checker of sprite enable changes.
void sync(EmuTime::param time)
Update VRAM state to specified moment in time.
Definition VDPVRAM.hh:416
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:67
byte getStatusReg0() const
Should only be used by SpriteChecker.
Definition VDP.hh:633
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
Definition VDP.hh:76
void setSpriteStatus(byte value)
Should only be used by SpriteChecker.
Definition VDP.hh:643
bool isMSX1VDP() const
Is this an MSX1 VDP?
Definition VDP.hh:108
Interface that can be registered at VRAMWindow, to be called when the contents of the VRAM inside tha...
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:315
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:481
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION)
Contains all the information to draw a line of a sprite.
SpritePattern pattern
Pattern of this sprite line, corrected for magnification.
byte colorAttrib
Bit 3..0 are index in palette.
int16_t x
X-coordinate of sprite, corrected for early clock.
#define UNREACHABLE