23 : vdp(vdp_), vram(vdp.getVRAM())
24 , limitSpritesSetting(renderSettings.getLimitSpritesSetting())
25 , frameStartTime(time)
39 updateSpritesMethod = &SpriteChecker::updateSprites1;
48 a = (a | (a >> 8)) & 0xFF00FF00;
49 a = (a | (a >> 4)) & 0xF0F0F0F0;
50 a = (a | (a >> 2)) & 0xCCCCCCCC;
51 a = (a | (a >> 1)) & 0xAAAAAAAA;
56 unsigned patternNr,
unsigned y)
59 unsigned index = patternNr * 8 + y;
62 pattern |= patternPtr[index + 16] << 16;
64 return !vdp.
isSpriteMag() ? pattern : doublePattern(pattern);
67 unsigned patternNr,
unsigned y)
70 unsigned index = patternNr * 8 + y;
71 auto patternPtr = (index & 1) ? ptr1 : ptr0;
75 pattern |= patternPtr[index + (16 / 2)] << 16;
77 return !vdp.
isSpriteMag() ? pattern : doublePattern(pattern);
80void SpriteChecker::updateSprites1(
int limit)
85 checkSprites1(currentLine, limit);
89 if ((currentLine <= l0) && (l0 < limit)) {
90 checkSprites1(l0, l0 + 1);
97inline void SpriteChecker::checkSprites1(
int minLine,
int maxLine)
121 bool limitSprites = limitSpritesSetting.
getBoolean();
124 int magSize = (mag + 1) *
size;
126 byte patternIndexMask =
size == 16 ? 0xFC : 0xFF;
127 int fifthSpriteNum = -1;
128 int fifthSpriteLine = 999;
131 for (; sprite < 32; ++sprite) {
132 int y = attributePtr[4 * sprite + 0];
135 for (
int line = minLine; line < maxLine; ++line) {
137 int displayLine = line + displayDelta;
138 int spriteLine = (displayLine - y) & 0xFF;
139 if (spriteLine >= magSize) {
141 line += 256 - spriteLine - 1;
145 auto visibleIndex = spriteCount[line];
146 if (visibleIndex == 4) {
148 if (line < fifthSpriteLine) {
149 fifthSpriteLine = line;
150 fifthSpriteNum = sprite;
152 if (limitSprites)
continue;
155 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
156 int patternIndex = attributePtr[4 * sprite + 2] & patternIndexMask;
157 if (mag) spriteLine /= 2;
158 sip.pattern = calculatePatternNP(patternIndex, spriteLine);
159 sip.x = attributePtr[4 * sprite + 1];
160 byte colorAttrib = attributePtr[4 * sprite + 3];
161 if (colorAttrib & 0x80) sip.x -= 32;
162 sip.colorAttrib = colorAttrib;
164 spriteCount[line] = visibleIndex + 1;
170 if (fifthSpriteNum != -1) {
174 if ((status & 0xC0) == 0) {
175 status =
byte(0x40 | (status & 0x20) | fifthSpriteNum);
178 if (~status & 0x40) {
180 status = (status & 0x20) |
byte(
std::min(sprite, 31));
209 for (
auto line :
xrange(minLine, maxLine)) {
210 int minXCollision = 999;
211 for (
int i = std::min<int>(4, spriteCount[line]); --i >= 1; ) {
212 auto color1 = spriteBuffer[line][i].colorAttrib & 0xf;
213 if (!can0collide && (color1 == 0))
continue;
214 int x_i = spriteBuffer[line][i].x;
216 for (
int j = i; --j >= 0; ) {
217 auto color2 = spriteBuffer[line][j].colorAttrib & 0xf;
218 if (!can0collide && (color2 == 0))
continue;
220 int x_j = spriteBuffer[line][j].x;
221 int dist = x_j - x_i;
222 if ((-magSize < dist) && (dist < magSize)) {
232 colPat &= (1 << (32 + x_i)) - 1;
235 int xCollision = x_i + std::countl_zero(colPat);
236 assert(xCollision >= 0);
237 minXCollision =
std::min(minXCollision, xCollision);
242 if (minXCollision < 256) {
248 collisionX = minXCollision + 12;
255void SpriteChecker::updateSprites2(
int limit)
261 checkSprites2(currentLine, limit);
265 if ((currentLine <= l0) && (l0 < limit)) {
266 checkSprites2(l0, l0 + 1);
273inline void SpriteChecker::checkSprites2(
int minLine,
int maxLine)
283 bool limitSprites = limitSpritesSetting.
getBoolean();
286 int magSize = (mag + 1) *
size;
287 int patternIndexMask = (
size == 16) ? 0xFC : 0xFF;
288 int ninthSpriteNum = -1;
289 int ninthSpriteLine = 999;
295 auto [attributePtr0, attributePtr1] =
298 for (; sprite < 32; ++sprite) {
299 int y = attributePtr0[2 * sprite + 0];
302 for (
int line = minLine; line < maxLine; ++line) {
304 int displayLine = line + displayDelta;
305 int spriteLine = (displayLine - y) & 0xFF;
306 if (spriteLine >= magSize) {
308 line += 256 - spriteLine - 1;
312 auto visibleIndex = spriteCount[line];
313 if (visibleIndex == 8) {
315 if (line < ninthSpriteLine) {
316 ninthSpriteLine = line;
317 ninthSpriteNum = sprite;
319 if (limitSprites)
continue;
322 if (mag) spriteLine /= 2;
323 unsigned colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
327 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
328 int patternIndex = attributePtr0[2 * sprite + 1] & patternIndexMask;
329 sip.pattern = calculatePatternPlanar(patternIndex, spriteLine);
330 sip.x = attributePtr1[2 * sprite + 0];
331 if (colorAttrib & 0x80) sip.x -= 32;
332 sip.colorAttrib = colorAttrib;
335 spriteBuffer[line][visibleIndex + 1].colorAttrib = 0;
336 spriteCount[line] = visibleIndex + 1;
343 for (; sprite < 32; ++sprite) {
344 int y = attributePtr0[4 * sprite + 0];
347 for (
int line = minLine; line < maxLine; ++line) {
349 int displayLine = line + displayDelta;
350 int spriteLine = (displayLine - y) & 0xFF;
351 if (spriteLine >= magSize) {
353 line += 256 - spriteLine - 1;
357 auto visibleIndex = spriteCount[line];
358 if (visibleIndex == 8) {
360 if (line < ninthSpriteLine) {
361 ninthSpriteLine = line;
362 ninthSpriteNum = sprite;
364 if (limitSprites)
continue;
367 if (mag) spriteLine /= 2;
368 unsigned colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
377 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
378 int patternIndex = attributePtr0[4 * sprite + 2] & patternIndexMask;
379 sip.pattern = calculatePatternNP(patternIndex, spriteLine);
380 sip.x = attributePtr0[4 * sprite + 1];
381 if (colorAttrib & 0x80) sip.x -= 32;
382 sip.colorAttrib = colorAttrib;
392 spriteBuffer[line][visibleIndex + 1].colorAttrib = 0;
393 spriteCount[line] = visibleIndex + 1;
400 if (ninthSpriteNum != -1) {
405 if ((status & 0xC0) == 0) {
406 status =
byte(0x40 | (status & 0x20) | ninthSpriteNum);
409 if (~status & 0x40) {
411 status = (status & 0x20) |
byte(
std::min(sprite, 31));
442 for (
auto line :
xrange(minLine, maxLine)) {
443 int minXCollision = 999;
444 std::span<SpriteInfo, 32 + 1> visibleSprites = spriteBuffer[line];
445 for (
int i = std::min<int>(8, spriteCount[line]); --i >= 1; ) {
446 auto colorAttrib1 = visibleSprites[i].colorAttrib;
447 if (!can0collide && ((colorAttrib1 & 0xf) == 0))
continue;
449 if (colorAttrib1 & 0x60)
continue;
451 int x_i = visibleSprites[i].x;
453 for (
int j = i; --j >= 0; ) {
454 auto colorAttrib2 = visibleSprites[j].colorAttrib;
455 if (!can0collide && ((colorAttrib2 & 0xf) == 0))
continue;
457 if (colorAttrib2 & 0x60)
continue;
460 int x_j = visibleSprites[j].x;
461 int dist = x_j - x_i;
462 if ((-magSize < dist) && (dist < magSize)) {
472 colPat &= (1 << (32 + x_i)) - 1;
475 int xCollision = x_i + std::countl_zero(colPat);
476 assert(xCollision >= 0);
477 minXCollision =
std::min(minXCollision, xCollision);
482 if (minXCollision < 256) {
486 collisionX = minXCollision + 12;
495template<
typename Archive>
498 if constexpr (Archive::IS_LOADER) {
513 ar.serialize(
"collisionX", collisionX,
514 "collisionY", collisionY);
515 if (ar.versionAtLeast(version, 2)) {
516 ar.serialize(
"currentLine", currentLine);
bool getBoolean() const noexcept
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
Class containing all settings for renderers.
void frameStart(EmuTime::param time)
Signals the start of a new frame.
void reset(EmuTime::param time)
Puts the sprite checker in its initial state.
void serialize(Archive &ar, unsigned version)
uint32_t SpritePattern
Bitmap of length 32 describing a sprite pattern.
SpriteChecker(VDP &vdp, RenderSettings &renderSettings, EmuTime::param time)
Create a sprite checker.
VRAMWindow spriteAttribTable
VRAMWindow spritePatternTable
Unified implementation of MSX Video Display Processors (VDPs).
int getSpriteSize() const
Gets the sprite size in pixels (8/16).
byte getStatusReg0() const
Should only be used by SpriteChecker.
bool spritesEnabledFast() const
Same as spritesEnabled(), but may only be called in sprite mode 1 or 2.
void setSpriteStatus(byte value)
Should only be used by SpriteChecker.
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
int getLineZero() const
Get the absolute line number of display line zero.
byte getVerticalScroll() const
Gets the current vertical scroll (line displayed at Y=0).
EmuTime::param getFrameStartTime() const
bool canSpriteColor0Collide() const
Can a sprite which has color=0 collide with some other sprite?
bool isSpriteMag() const
Are sprites magnified?
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
byte readNP(unsigned index) const
Reads a byte from VRAM in its current state.
byte readPlanar(unsigned index) const
Similar to readNP, but now with planar addressing.
std::pair< std::span< const byte, size/2 >, std::span< const byte, size/2 > > getReadAreaPlanar(unsigned index) const
Similar to getReadArea(), but now with planar addressing mode.
void setObserver(VRAMObserver *newObserver)
Register an observer on this VRAM window.
std::span< const byte, size > getReadArea(unsigned index) const
Gets a span of a contiguous part of the VRAM.
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
constexpr void fill(ForwardRange &&range, const T &value)
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)