22 : vdp(vdp_), vram(vdp.getVRAM())
23 , limitSpritesSetting(renderSettings.getLimitSpritesSetting())
24 , frameStartTime(time)
38 updateSpritesMethod = &SpriteChecker::updateSprites1;
47 a = (a | (a >> 8)) & 0xFF00FF00;
48 a = (a | (a >> 4)) & 0xF0F0F0F0;
49 a = (a | (a >> 2)) & 0xCCCCCCCC;
50 a = (a | (a >> 1)) & 0xAAAAAAAA;
55 unsigned patternNr,
unsigned y)
58 unsigned index = patternNr * 8 + y;
61 pattern |= patternPtr[index + 16] << 16;
63 return !vdp.
isSpriteMag() ? pattern : doublePattern(pattern);
66 unsigned patternNr,
unsigned y)
71 unsigned index = patternNr * 8 + y;
72 const byte* patternPtr = (index & 1) ? ptr1 : ptr0;
76 pattern |= patternPtr[index + (16 / 2)] << 16;
78 return !vdp.
isSpriteMag() ? pattern : doublePattern(pattern);
81 void SpriteChecker::updateSprites1(
int limit)
86 checkSprites1(currentLine, limit);
90 if ((currentLine <= l0) && (l0 < limit)) {
91 checkSprites1(l0, l0 + 1);
98 inline void SpriteChecker::checkSprites1(
int minLine,
int maxLine)
122 bool limitSprites = limitSpritesSetting.
getBoolean();
125 int magSize = (mag + 1) * size;
127 byte patternIndexMask = size == 16 ? 0xFC : 0xFF;
128 int fifthSpriteNum = -1;
129 int fifthSpriteLine = 999;
132 for (; sprite < 32; ++sprite) {
133 int y = attributePtr[4 * sprite + 0];
136 for (
int line = minLine; line < maxLine; ++line) {
138 int displayLine = line + displayDelta;
139 int spriteLine = (displayLine - y) & 0xFF;
140 if (spriteLine >= magSize) {
142 line += 256 - spriteLine - 1;
146 int visibleIndex = spriteCount[line];
147 if (visibleIndex == 4) {
149 if (line < fifthSpriteLine) {
150 fifthSpriteLine = line;
151 fifthSpriteNum = sprite;
153 if (limitSprites)
continue;
156 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
157 int patternIndex = attributePtr[4 * sprite + 2] & patternIndexMask;
158 if (mag) spriteLine /= 2;
159 sip.
pattern = calculatePatternNP(patternIndex, spriteLine);
160 sip.
x = attributePtr[4 * sprite + 1];
161 byte colorAttrib = attributePtr[4 * sprite + 3];
162 if (colorAttrib & 0x80) sip.
x -= 32;
165 spriteCount[line] = visibleIndex + 1;
171 if (fifthSpriteNum != -1) {
175 if ((status & 0xC0) == 0) {
176 status = 0x40 | (status & 0x20) | fifthSpriteNum;
179 if (~status & 0x40) {
181 status = (status & 0x20) |
std::min(sprite, 31);
210 for (
int line = minLine; line < maxLine; ++line) {
211 int minXCollision = 999;
212 for (
int i = std::min<int>(4, spriteCount[line]); --i >= 1; ) {
213 auto color1 = spriteBuffer[line][i].
colorAttrib & 0xf;
214 if (tp && (color1 == 0))
continue;
215 int x_i = spriteBuffer[line][i].
x;
217 for (
int j = i; --j >= 0; ) {
218 auto color2 = spriteBuffer[line][j].
colorAttrib & 0xf;
219 if (tp && (color2 == 0))
continue;
221 int x_j = spriteBuffer[line][j].
x;
222 int dist = x_j - x_i;
223 if ((-magSize < dist) && (dist < magSize)) {
233 colPat &= (1 << (32 + x_i)) - 1;
237 assert(xCollision >= 0);
238 minXCollision =
std::min(minXCollision, xCollision);
243 if (minXCollision < 256) {
249 collisionX = minXCollision + 12;
256 void SpriteChecker::updateSprites2(
int limit)
262 checkSprites2(currentLine, limit);
266 if ((currentLine <= l0) && (l0 < limit)) {
267 checkSprites2(l0, l0 + 1);
274 inline void SpriteChecker::checkSprites2(
int minLine,
int maxLine)
284 bool limitSprites = limitSpritesSetting.
getBoolean();
287 int magSize = (mag + 1) * size;
288 int patternIndexMask = (size == 16) ? 0xFC : 0xFF;
289 int ninthSpriteNum = -1;
290 int ninthSpriteLine = 999;
296 const byte* attributePtr0;
297 const byte* attributePtr1;
299 512, 32 * 4, attributePtr0, attributePtr1);
301 for (; sprite < 32; ++sprite) {
302 int y = attributePtr0[2 * sprite + 0];
305 for (
int line = minLine; line < maxLine; ++line) {
307 int displayLine = line + displayDelta;
308 int spriteLine = (displayLine - y) & 0xFF;
309 if (spriteLine >= magSize) {
311 line += 256 - spriteLine - 1;
315 int visibleIndex = spriteCount[line];
316 if (visibleIndex == 8) {
318 if (line < ninthSpriteLine) {
319 ninthSpriteLine = line;
320 ninthSpriteNum = sprite;
322 if (limitSprites)
continue;
325 if (mag) spriteLine /= 2;
326 int colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
330 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
331 int patternIndex = attributePtr0[2 * sprite + 1] & patternIndexMask;
332 sip.
pattern = calculatePatternPlanar(patternIndex, spriteLine);
333 sip.
x = attributePtr1[2 * sprite + 0];
334 if (colorAttrib & 0x80) sip.
x -= 32;
338 spriteBuffer[line][visibleIndex + 1].
colorAttrib = 0;
339 spriteCount[line] = visibleIndex + 1;
343 const byte* attributePtr0 =
346 for (; sprite < 32; ++sprite) {
347 int y = attributePtr0[4 * sprite + 0];
350 for (
int line = minLine; line < maxLine; ++line) {
352 int displayLine = line + displayDelta;
353 int spriteLine = (displayLine - y) & 0xFF;
354 if (spriteLine >= magSize) {
356 line += 256 - spriteLine - 1;
360 int visibleIndex = spriteCount[line];
361 if (visibleIndex == 8) {
363 if (line < ninthSpriteLine) {
364 ninthSpriteLine = line;
365 ninthSpriteNum = sprite;
367 if (limitSprites)
continue;
370 if (mag) spriteLine /= 2;
371 int colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
380 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
381 int patternIndex = attributePtr0[4 * sprite + 2] & patternIndexMask;
382 sip.
pattern = calculatePatternNP(patternIndex, spriteLine);
383 sip.
x = attributePtr0[4 * sprite + 1];
384 if (colorAttrib & 0x80) sip.
x -= 32;
395 spriteBuffer[line][visibleIndex + 1].
colorAttrib = 0;
396 spriteCount[line] = visibleIndex + 1;
403 if (ninthSpriteNum != -1) {
408 if ((status & 0xC0) == 0) {
409 status = 0x40 | (status & 0x20) | ninthSpriteNum;
412 if (~status & 0x40) {
414 status = (status & 0x20) |
std::min(sprite, 31);
445 for (
int line = minLine; line < maxLine; ++line) {
446 int minXCollision = 999;
447 SpriteInfo* visibleSprites = spriteBuffer[line];
448 for (
int i = std::min<int>(8, spriteCount[line]); --i >= 1; ) {
450 if (tp && ((colorAttrib1 & 0xf) == 0))
continue;
452 if (colorAttrib1 & 0x60)
continue;
454 int x_i = visibleSprites[i].
x;
456 for (
int j = i; --j >= 0; ) {
458 if (tp && ((colorAttrib2 & 0xf) == 0))
continue;
460 if (colorAttrib2 & 0x60)
continue;
463 int x_j = visibleSprites[j].
x;
464 int dist = x_j - x_i;
465 if ((-magSize < dist) && (dist < magSize)) {
475 colPat &= (1 << (32 + x_i)) - 1;
479 assert(xCollision >= 0);
480 minXCollision =
std::min(minXCollision, xCollision);
485 if (minXCollision < 256) {
489 collisionX = minXCollision + 12;
498 template<
typename Archive>
516 ar.serialize(
"collisionX", collisionX,
517 "collisionY", collisionY);
518 if (ar.versionAtLeast(version, 2)) {
519 ar.serialize(
"currentLine", currentLine);
void reset(EmuTime::param time)
Puts the sprite checker in its initial state.
void setSpriteStatus(byte value)
Should only be used by SpriteChecker.
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
EmuTime::param getFrameStartTime() const
byte colorAttrib
Bit 3..0 are index in palette.
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
byte readNP(unsigned index) const
Reads a byte from VRAM in its current state.
uint8_t byte
8 bit unsigned integer
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).
VRAMWindow spriteAttribTable
void setObserver(VRAMObserver *newObserver)
Register an observer on this VRAM window.
size_t size(std::string_view utf8)
void fill(ForwardRange &&range, const T &value)
void serialize(Archive &ar, unsigned version)
bool getBoolean() const noexcept
SpritePattern pattern
Pattern of this sprite line, corrected for magnification.
void frameStart(EmuTime::param time)
Signals the start of a new frame.
bool getTransparency() const
Gets the current transparency setting.
uint32_t SpritePattern
Bitmap of length 32 describing a sprite pattern.
bool isSpriteMag() const
Are sprites magnified?
Thanks to enen for testing this on a real cartridge:
VRAMWindow spritePatternTable
int getSpriteSize() const
Gets the sprite size in pixels (8/16).
Contains all the information to draw a line of a sprite.
byte getStatusReg0() const
Should only be used by SpriteChecker.
Unified implementation of MSX Video Display Processors (VDPs).
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
bool spritesEnabledFast() const
Same as spritesEnabled(), but may only be called in sprite mode 1 or 2.
Class containing all settings for renderers.
const byte * getReadArea(unsigned index, unsigned size) const
Gets a pointer to a contiguous part of the VRAM.
byte readPlanar(unsigned index) const
Similar to readNP, but now with planar addressing.
void getReadAreaPlanar(unsigned index, unsigned size, const byte *&ptr0, const byte *&ptr1) const
Similar to getReadArea(), but now with planar addressing mode.
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
int16_t x
X-coordinate of sprite, corrected for early clock.
SpriteChecker(VDP &vdp, RenderSettings &renderSettings, EmuTime::param time)
Create a sprite checker.
unsigned countLeadingZeros(unsigned x)
Count the number of leading zero-bits in the given word.