23 : vdp(vdp_), vram(vdp.getVRAM())
24 , limitSpritesSetting(renderSettings.getLimitSpritesSetting())
25 , frameStartTime(time)
39 updateSpritesMethod = &SpriteChecker::updateSprites1;
43 unsigned patternNr,
unsigned y)
const
46 unsigned index = patternNr * 8 + y;
49 pattern |= patternPtr[index + 16] << 16;
54 unsigned patternNr,
unsigned y)
const
57 unsigned index = patternNr * 8 + y;
58 auto patternPtr = (index & 1) ? ptr1 : ptr0;
62 pattern |= patternPtr[index + (16 / 2)] << 16;
67void SpriteChecker::updateSprites1(
int limit)
72 checkSprites1(currentLine, limit);
76 if ((currentLine <= l0) && (l0 < limit)) {
77 checkSprites1(l0, l0 + 1);
84inline void SpriteChecker::checkSprites1(
int minLine,
int maxLine)
108 bool limitSprites = limitSpritesSetting.
getBoolean();
111 int magSize = (mag + 1) * size;
113 byte patternIndexMask =
size == 16 ? 0xFC : 0xFF;
114 int fifthSpriteNum = -1;
115 int fifthSpriteLine = 999;
118 for (; sprite < 32; ++sprite) {
119 int y = attributePtr[4 * sprite + 0];
122 for (
int line = minLine; line < maxLine; ++line) {
124 int displayLine = line + displayDelta;
125 int spriteLine = (displayLine - y) & 0xFF;
126 if (spriteLine >= magSize) {
128 line += 256 - spriteLine - 1;
132 auto visibleIndex = spriteCount[line];
133 if (visibleIndex == 4) {
135 if (line < fifthSpriteLine) {
136 fifthSpriteLine = line;
137 fifthSpriteNum = sprite;
139 if (limitSprites)
continue;
142 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
143 int patternIndex = attributePtr[4 * sprite + 2] & patternIndexMask;
144 if (mag) spriteLine /= 2;
145 sip.pattern = calculatePatternNP(patternIndex, spriteLine);
146 sip.x = attributePtr[4 * sprite + 1];
147 byte colorAttrib = attributePtr[4 * sprite + 3];
148 if (colorAttrib & 0x80) sip.x -= 32;
149 sip.colorAttrib = colorAttrib;
151 spriteCount[line] = visibleIndex + 1;
157 if (fifthSpriteNum != -1) {
161 if ((status & 0xC0) == 0) {
162 status =
byte(0x40 | (status & 0x20) | fifthSpriteNum);
165 if (~status & 0x40) {
167 status = (status & 0x20) |
byte(std::min(sprite, 31));
196 for (
auto line :
xrange(minLine, maxLine)) {
197 int minXCollision = 999;
198 for (
int i = std::min<int>(4, spriteCount[line]); --i >= 1; ) {
199 auto color1 = spriteBuffer[line][i].colorAttrib & 0xf;
200 if (!can0collide && (color1 == 0))
continue;
201 int x_i = spriteBuffer[line][i].x;
203 for (
int j = i; --j >= 0; ) {
204 auto color2 = spriteBuffer[line][j].colorAttrib & 0xf;
205 if (!can0collide && (color2 == 0))
continue;
207 int x_j = spriteBuffer[line][j].x;
208 int dist = x_j - x_i;
209 if ((-magSize < dist) && (dist < magSize)) {
219 colPat &= (1 << (32 + x_i)) - 1;
222 int xCollision = x_i + std::countl_zero(colPat);
223 assert(xCollision >= 0);
224 minXCollision = std::min(minXCollision, xCollision);
229 if (minXCollision < 256) {
235 collisionX = minXCollision + 12;
242void SpriteChecker::updateSprites2(
int limit)
248 checkSprites2(currentLine, limit);
252 if ((currentLine <= l0) && (l0 < limit)) {
253 checkSprites2(l0, l0 + 1);
260inline void SpriteChecker::checkSprites2(
int minLine,
int maxLine)
270 bool limitSprites = limitSpritesSetting.
getBoolean();
273 int magSize = (mag + 1) * size;
274 int patternIndexMask = (
size == 16) ? 0xFC : 0xFF;
275 int ninthSpriteNum = -1;
276 int ninthSpriteLine = 999;
282 auto [attributePtr0, attributePtr1] =
285 for (; sprite < 32; ++sprite) {
286 int y = attributePtr0[2 * sprite + 0];
289 for (
int line = minLine; line < maxLine; ++line) {
291 int displayLine = line + displayDelta;
292 int spriteLine = (displayLine - y) & 0xFF;
293 if (spriteLine >= magSize) {
295 line += 256 - spriteLine - 1;
299 auto visibleIndex = spriteCount[line];
300 if (visibleIndex == 8) {
302 if (line < ninthSpriteLine) {
303 ninthSpriteLine = line;
304 ninthSpriteNum = sprite;
306 if (limitSprites)
continue;
309 if (mag) spriteLine /= 2;
310 unsigned colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
314 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
315 int patternIndex = attributePtr0[2 * sprite + 1] & patternIndexMask;
316 sip.pattern = calculatePatternPlanar(patternIndex, spriteLine);
317 sip.x = attributePtr1[2 * sprite + 0];
318 if (colorAttrib & 0x80) sip.x -= 32;
319 sip.colorAttrib = colorAttrib;
322 spriteBuffer[line][visibleIndex + 1].colorAttrib = 0;
323 spriteCount[line] = visibleIndex + 1;
330 for (; sprite < 32; ++sprite) {
331 int y = attributePtr0[4 * sprite + 0];
334 for (
int line = minLine; line < maxLine; ++line) {
336 int displayLine = line + displayDelta;
337 int spriteLine = (displayLine - y) & 0xFF;
338 if (spriteLine >= magSize) {
340 line += 256 - spriteLine - 1;
344 auto visibleIndex = spriteCount[line];
345 if (visibleIndex == 8) {
347 if (line < ninthSpriteLine) {
348 ninthSpriteLine = line;
349 ninthSpriteNum = sprite;
351 if (limitSprites)
continue;
354 if (mag) spriteLine /= 2;
355 unsigned colorIndex = (~0u << 10) | (sprite * 16 + spriteLine);
364 SpriteInfo& sip = spriteBuffer[line][visibleIndex];
365 int patternIndex = attributePtr0[4 * sprite + 2] & patternIndexMask;
366 sip.pattern = calculatePatternNP(patternIndex, spriteLine);
367 sip.x = attributePtr0[4 * sprite + 1];
368 if (colorAttrib & 0x80) sip.x -= 32;
369 sip.colorAttrib = colorAttrib;
379 spriteBuffer[line][visibleIndex + 1].colorAttrib = 0;
380 spriteCount[line] = visibleIndex + 1;
387 if (ninthSpriteNum != -1) {
392 if ((status & 0xC0) == 0) {
393 status =
byte(0x40 | (status & 0x20) | ninthSpriteNum);
396 if (~status & 0x40) {
398 status = (status & 0x20) |
byte(std::min(sprite, 31));
429 for (
auto line :
xrange(minLine, maxLine)) {
430 int minXCollision = 999;
431 std::span<SpriteInfo, 32 + 1> visibleSprites = spriteBuffer[line];
432 for (
int i = std::min<int>(8, spriteCount[line]); --i >= 1; ) {
433 auto colorAttrib1 = visibleSprites[i].colorAttrib;
434 if (!can0collide && ((colorAttrib1 & 0xf) == 0))
continue;
436 if (colorAttrib1 & 0x60)
continue;
438 int x_i = visibleSprites[i].x;
440 for (
int j = i; --j >= 0; ) {
441 auto colorAttrib2 = visibleSprites[j].colorAttrib;
442 if (!can0collide && ((colorAttrib2 & 0xf) == 0))
continue;
444 if (colorAttrib2 & 0x60)
continue;
447 int x_j = visibleSprites[j].x;
448 int dist = x_j - x_i;
449 if ((-magSize < dist) && (dist < magSize)) {
459 colPat &= (1 << (32 + x_i)) - 1;
462 int xCollision = x_i + std::countl_zero(colPat);
463 assert(xCollision >= 0);
464 minXCollision = std::min(minXCollision, xCollision);
469 if (minXCollision < 256) {
473 collisionX = minXCollision + 12;
482template<
typename Archive>
485 if constexpr (Archive::IS_LOADER) {
500 ar.serialize(
"collisionX", collisionX,
501 "collisionY", collisionY);
502 if (ar.versionAtLeast(version, 2)) {
503 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.
static constexpr SpritePattern doublePattern(SpritePattern a)
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.
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)