From a3f2c6e20b491d9dda07c56cb73ccdc6a8d495ee Mon Sep 17 00:00:00 2001 From: Ian Burgmyer Date: Thu, 4 Mar 2021 23:06:01 -0500 Subject: [PATCH] Sprites kinda work now. * Priorities still haven't been implemented yet. --- ISSUES.txt | 3 +++ libplip/Core/GameBoy/GameBoyInstance.Video.cpp | 24 +++++++++++++++--------- libplip/Core/GameBoy/GameBoyInstance.h | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ISSUES.txt b/ISSUES.txt index a284ceb..953e1d7 100644 --- a/ISSUES.txt +++ b/ISSUES.txt @@ -1,3 +1,6 @@ +* Sprites + - Sprite priority and conflict resolution is NYI. + * Various timing issues. - Check over the DMG timer routines. - OAM DMA transfer seems to end early. diff --git a/libplip/Core/GameBoy/GameBoyInstance.Video.cpp b/libplip/Core/GameBoy/GameBoyInstance.Video.cpp index 6fb706a..7a6fa29 100644 --- a/libplip/Core/GameBoy/GameBoyInstance.Video.cpp +++ b/libplip/Core/GameBoy/GameBoyInstance.Video.cpp @@ -97,16 +97,16 @@ namespace Plip::Core::GameBoy { // Just do everything at once since the OAM will be locked at this point. m_spriteListIdx = 0; memset(m_spriteList, 0xFF, m_maxSpritesPerScanline); - for(auto sprIdx = 0; sprIdx < 0; sprIdx++) { + for(auto sprIdx = 0; sprIdx < m_maxSpritesInOam; sprIdx++) { auto sprAddr = 4 * sprIdx; auto sprY = m_oam->GetByte(sprAddr); auto sprX = m_oam->GetByte(sprAddr + 1); if(sprX == 0 || sprX >= m_screenWidth + 8) continue; - if(m_videoLy >= sprY - spriteHeight || m_videoLy < sprY - (16 - spriteHeight)) { - m_spriteList[m_spriteListIdx++] = sprIdx; - } + if(m_videoLy < sprY - 16 || m_videoLy >= sprY - (16 - spriteHeight)) + continue; + m_spriteList[m_spriteListIdx++] = sprIdx; if(m_spriteListIdx == m_maxSpritesPerScanline) break; } @@ -397,23 +397,29 @@ namespace Plip::Core::GameBoy { auto sprPalette = BIT_TEST(sprAttr, 4) ? obp1 : obp0; // Position and tile number. - auto sprX = m_oam->GetByte(base, true); - auto sprY = m_oam->GetByte(base + 1, true); + auto sprY = m_oam->GetByte(base, true); + auto sprX = m_oam->GetByte(base + 1, true); tileIdx = m_oam->GetByte(base + 2, true); if(doubleHeight) tileIdx &= 0b11111110; // LSB is ignored for 8x16 sprites. // Check to see if the sprite should even be drawn. - if(sprX - 8 < scx || sprX + 8 > scx) continue; // X value out of range. + if(m_videoLx < sprX - 8 || m_videoLx >= sprX) continue; // X value out of range. if(!sprPriority && pixelColor > 0) continue; // Background/window overlaps sprite. // Figure out which pixel should be considered. auto sprHeight = doubleHeight ? 16 : 8; - tilePX = (sprFlipX ? 8 : 0) - scx - (sprX - 8); - tilePY = (sprFlipY ? sprHeight : 0) - scy - (sprY - 16); + tilePX = m_videoLx - (sprX - 8); + if(sprFlipX) tilePX += -7; + if(sprFlipY) tilePY += -(doubleHeight ? 15 : 7); + tilePY = m_videoLy - (sprY - 16); if(doubleHeight && tilePY >= 8) { // Move to the second tile of the 8x16 sprite. tilePY %= 8; tileIdx |= 0b1; + } else if(doubleHeight && tilePY < 8) { + // Move to the first tile (this will likely only be used if the sprite + // is flipped). + tileIdx &= 0b11111110; } lineOffset = tilePY * 2; diff --git a/libplip/Core/GameBoy/GameBoyInstance.h b/libplip/Core/GameBoy/GameBoyInstance.h index acc648d..6c9523e 100644 --- a/libplip/Core/GameBoy/GameBoyInstance.h +++ b/libplip/Core/GameBoy/GameBoyInstance.h @@ -205,6 +205,7 @@ namespace Plip::Core::GameBoy { static const uint32_t m_vramBgBlockOffset = 0x0400; static const uint8_t m_maxSpritesPerScanline = 10; + static const uint8_t m_maxSpritesInOam = 40; static const uint32_t m_videoHBlankTime = 0; static const uint32_t m_videoOamScanTime = 80;