diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 72f74091..dc8d348f 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -73,6 +73,8 @@ // // mosaic: // * mosaic grid starts at 0,0 regardless of the BG/sprite position +// * when changing it midframe: new X setting is applied immediately, new Y setting is applied only +// after the end of the current mosaic row // TODO: find which parts of DISPCNT are latched. for example, not possible to change video mode midframe. @@ -109,6 +111,10 @@ void GPU2D::Reset() BGMosaicSize[1] = 0; OBJMosaicSize[0] = 0; OBJMosaicSize[1] = 0; + BGMosaicY = 0; + BGMosaicYMax = 0; + OBJMosaicY = 0; + OBJMosaicYMax = 0; BlendCnt = 0; EVA = 16; @@ -624,6 +630,11 @@ void GPU2D::VBlankEnd() BGXRefInternal[1] = BGXRef[1]; BGYRefInternal[0] = BGYRef[0]; BGYRefInternal[1] = BGYRef[1]; + + BGMosaicY = 0; + BGMosaicYMax = BGMosaicSize[1]; + OBJMosaicY = 0; + OBJMosaicYMax = OBJMosaicSize[1]; } @@ -1179,6 +1190,22 @@ void GPU2D::DrawScanline_Mode1(u32 line, u32* dst) break; } } + + if (BGMosaicY >= BGMosaicYMax) + { + BGMosaicY = 0; + BGMosaicYMax = BGMosaicSize[1]; + } + else + BGMosaicY++; + + if (OBJMosaicY >= OBJMosaicYMax) + { + OBJMosaicY = 0; + OBJMosaicYMax = OBJMosaicSize[1]; + } + else + OBJMosaicY++; } @@ -1230,6 +1257,7 @@ void GPU2D::DrawBG_Text(u32 line, u32* dst, u32 bgnum) { u8* windowmask = (u8*)&dst[256*2]; u16 bgcnt = BGCnt[bgnum]; + u32 xmos = 0, xmossize = 0; u32 tilesetaddr, tilemapaddr; u16* pal; @@ -1238,6 +1266,13 @@ void GPU2D::DrawBG_Text(u32 line, u32* dst, u32 bgnum) u16 xoff = BGXPos[bgnum]; u16 yoff = BGYPos[bgnum] + line; + if (bgcnt & 0x0040) + { + // mosaic + yoff -= BGMosaicY; + xmossize = BGMosaicSize[0]; + } + u32 widexmask = (bgcnt & 0x4000) ? 0x100 : 0; extpal = (DispCnt & 0x40000000); @@ -1271,6 +1306,7 @@ void GPU2D::DrawBG_Text(u32 line, u32* dst, u32 bgnum) u16 curtile; u16* curpal; u32 pixelsaddr; + u8 color; if (bgcnt & 0x0080) { @@ -1306,9 +1342,14 @@ void GPU2D::DrawBG_Text(u32 line, u32* dst, u32 bgnum) // draw pixel if (windowmask[i] & (1<(pixelsaddr + tilexoff); + if (xmos == 0) + { + u32 tilexoff = (curtile & 0x0400) ? (7-(xoff&0x7)) : (xoff&0x7); + color = GPU::ReadVRAM_BG(pixelsaddr + tilexoff); + xmos = xmossize; + } + else + xmos--; if (color) DrawPixel(&dst[i], curpal[color], 0x01000000<(pixelsaddr + (tilexoff >> 1)) >> 4; + u32 tilexoff = (curtile & 0x0400) ? (7-(xoff&0x7)) : (xoff&0x7); + if (tilexoff & 0x1) + { + color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) >> 4; + } + else + { + color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) & 0x0F; + } + xmos = xmossize; } else - { - color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) & 0x0F; - } + xmos--; if (color) DrawPixel(&dst[i], curpal[color], 0x01000000<(tilemapaddr + ((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11))); + if (xmos > 0) + { + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<> 8) & 0x7; - u32 tileyoff = (rotY >> 8) & 0x7; + xmos--; + } + else + if ((!((rotX|rotY) & overflowmask))) + { + curtile = GPU::ReadVRAM_BG(tilemapaddr + ((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11))); - color = GPU::ReadVRAM_BG(tilesetaddr + (curtile << 6) + (tileyoff << 3) + tilexoff); + // draw pixel + u32 tilexoff = (rotX >> 8) & 0x7; + u32 tileyoff = (rotY >> 8) & 0x7; - if (color) - DrawPixel(&dst[i], pal[color], 0x01000000<(tilesetaddr + (curtile << 6) + (tileyoff << 3) + tilexoff); + + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<(tilemapaddr + (((((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)) << 1)); + if (xmos > 0) + { + if (color & 0x8000) + DrawPixel(&dst[i], color, 0x01000000<(tilemapaddr + (((((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)) << 1)); + + if (color & 0x8000) + DrawPixel(&dst[i], color, 0x01000000<(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); + if (xmos > 0) + { + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); + + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<(tilemapaddr + (((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11)) << 1)); + if (xmos > 0) + { + if (color) + DrawPixel(&dst[i], curpal[color], 0x01000000<>12); - else curpal = pal; + xmos--; + } + else + if ((!((rotX|rotY) & overflowmask))) + { + curtile = GPU::ReadVRAM_BG(tilemapaddr + (((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11)) << 1)); - // draw pixel - u8 color; - u32 tilexoff = (rotX >> 8) & 0x7; - u32 tileyoff = (rotY >> 8) & 0x7; + if (extpal) curpal = GetBGExtPal(bgnum, curtile>>12); + else curpal = pal; - if (curtile & 0x0400) tilexoff = 7-tilexoff; - if (curtile & 0x0800) tileyoff = 7-tileyoff; + // draw pixel + u32 tilexoff = (rotX >> 8) & 0x7; + u32 tileyoff = (rotY >> 8) & 0x7; - color = GPU::ReadVRAM_BG(tilesetaddr + ((curtile & 0x03FF) << 6) + (tileyoff << 3) + tilexoff); + if (curtile & 0x0400) tilexoff = 7-tilexoff; + if (curtile & 0x0800) tileyoff = 7-tileyoff; - if (color) - DrawPixel(&dst[i], curpal[color], 0x01000000<(tilesetaddr + ((curtile & 0x03FF) << 6) + (tileyoff << 3) + tilexoff); + + if (color) + DrawPixel(&dst[i], curpal[color], 0x01000000<(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); + if (xmos > 0) + { + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<<2); - if (color) - DrawPixel(&dst[i], pal[color], 0x01000000<<2); + xmos--; + } + else + if (!(rotX & ofxmask) && !(rotY & ofymask)) + { + color = GPU::ReadVRAM_BG(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); + + if (color) + DrawPixel(&dst[i], pal[color], 0x01000000<<2); + } } rotX += rotA; @@ -1843,17 +1985,29 @@ void GPU2D::DrawSpritesWindow(u32 line, u8* dst) } template -void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, u32 ypos, u32* dst) +void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos, u32* dst) { u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000; u32 tilenum = attrib[2] & 0x03FF; u32 spritemode = window ? 0 : ((attrib[0] >> 10) & 0x3); + u32 xmos = 0, xmossize = 0; u32 ytilefactor; s32 centerX = boundwidth >> 1; s32 centerY = boundheight >> 1; + if (attrib[0] & 0x1000) + { + // mosaic + ypos -= OBJMosaicY; + if (ypos < 0) ypos = 0; + xmossize = OBJMosaicSize[0]; + + if (xpos > 0) + xmos = (xmossize+1) - (xpos % (xmossize+1)); + } + u32 xoff; if (xpos >= 0) { @@ -1878,6 +2032,8 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 width <<= 8; height <<= 8; + u16 color = 0; // transparent in all cases + if (spritemode == 3) { u32 alpha = attrib[2] >> 12; @@ -1914,12 +2070,20 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 } u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum; + if (xmos && !(attrib[0]&0x0200)) + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1)); for (; xoff < boundwidth;) { if ((u32)rotX < width && (u32)rotY < height) { - u16 color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1)); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1)); + xmos = xmossize; + } + else + xmos--; if (color & 0x8000) { @@ -1927,6 +2091,11 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 else dst[xpos] = color | prio; } } + else + { + if (xmos == 0) xmos = xmossize; + else xmos--; + } rotX += rotA; rotY += rotC; @@ -1965,11 +2134,20 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 else pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; } + if (xmos && !(attrib[0]&0x0200)) + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8)); + for (; xoff < boundwidth;) { if ((u32)rotX < width && (u32)rotY < height) { - u8 color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8)); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8)); + xmos = xmossize; + } + else + xmos--; if (color) { @@ -1977,6 +2155,11 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 else dst[xpos] = pal[color] | prio; } } + else + { + if (xmos == 0) xmos = xmossize; + else xmos--; + } rotX += rotA; rotY += rotC; @@ -1998,16 +2181,31 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 pal += (attrib[2] & 0xF000) >> 8; } + if (xmos && !(attrib[0]&0x0200)) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); + if (rotX & 0x100) + color >>= 4; + else + color &= 0x0F; + } + for (; xoff < boundwidth;) { if ((u32)rotX < width && (u32)rotY < height) { - u8 color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); + if (rotX & 0x100) + color >>= 4; + else + color &= 0x0F; - if (rotX & 0x100) - color >>= 4; + xmos = xmossize; + } else - color &= 0x0F; + xmos--; if (color) { @@ -2015,6 +2213,11 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 else dst[xpos] = pal[color] | prio; } } + else + { + if (xmos == 0) xmos = xmossize; + else xmos--; + } rotX += rotA; rotY += rotC; @@ -2026,14 +2229,26 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 } template -void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* dst) +void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* dst) { u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000; u32 tilenum = attrib[2] & 0x03FF; u32 spritemode = window ? 0 : ((attrib[0] >> 10) & 0x3); + u32 xmos = 0, xmossize = 0; u32 wmask = width - 8; // really ((width - 1) & ~0x7) + if (attrib[0] & 0x1000) + { + // mosaic + ypos -= OBJMosaicY; + if (ypos < 0) ypos = 0; + xmossize = OBJMosaicSize[0]; + + if (xpos > 0) + xmos = (xmossize+1) - (xpos % (xmossize+1)); + } + u32 xoff; u32 xend = width; if (xpos >= 0) @@ -2048,6 +2263,8 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d xpos = 0; } + u16 color = 0; // transparent in all cases + if (spritemode == 3) { // bitmap sprite @@ -2090,10 +2307,18 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d if (attrib[1] & 0x1000) { pixelsaddr += ((width-1 - xoff) << 1); + if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr); for (; xoff < xend;) { - u16 color = GPU::ReadVRAM_OBJ(pixelsaddr); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr); + xmos = xmossize; + } + else + xmos--; + pixelsaddr -= 2; if (color & 0x8000) @@ -2109,10 +2334,18 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d else { pixelsaddr += (xoff << 1); + if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr); for (; xoff < xend;) { - u16 color = GPU::ReadVRAM_OBJ(pixelsaddr); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr); + xmos = xmossize; + } + else + xmos--; + pixelsaddr += 2; if (color & 0x8000) @@ -2161,10 +2394,18 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d { pixelsaddr += (((width-1 - xoff) & wmask) << 3); pixelsaddr += ((width-1 - xoff) & 0x7); + if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr); for (; xoff < xend;) { - u8 color = GPU::ReadVRAM_OBJ(pixelsaddr); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr); + xmos = xmossize; + } + else + xmos--; + pixelsaddr--; if (color) @@ -2182,10 +2423,18 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d { pixelsaddr += ((xoff & wmask) << 3); pixelsaddr += (xoff & 0x7); + if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr); for (; xoff < xend;) { - u8 color = GPU::ReadVRAM_OBJ(pixelsaddr); + if (xmos == 0) + { + color = GPU::ReadVRAM_OBJ(pixelsaddr); + xmos = xmossize; + } + else + xmos--; + pixelsaddr++; if (color) @@ -2218,19 +2467,24 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d { pixelsaddr += (((width-1 - xoff) & wmask) << 2); pixelsaddr += (((width-1 - xoff) & 0x7) >> 1); + if (xmos) + { + if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; + else color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; + } for (; xoff < xend;) { - u8 color; - if (xoff & 0x1) + if (xmos == 0) { - color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; - pixelsaddr--; + if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; + else color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; + xmos = xmossize; } else - { - color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; - } + xmos--; + + if (xoff & 0x1) pixelsaddr--; if (color) { @@ -2247,19 +2501,24 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* d { pixelsaddr += ((xoff & wmask) << 2); pixelsaddr += ((xoff & 0x7) >> 1); + if (xmos) + { + if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; + else color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; + } for (; xoff < xend;) { - u8 color; - if (xoff & 0x1) + if (xmos == 0) { - color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; - pixelsaddr++; + if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; + else color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; + xmos = xmossize; } else - { - color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; - } + xmos--; + + pixelsaddr++; if (color) { diff --git a/src/GPU2D.h b/src/GPU2D.h index 3e9dc36b..d059fa06 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -93,6 +93,8 @@ private: u8 BGMosaicSize[2]; u8 OBJMosaicSize[2]; + u8 BGMosaicY, BGMosaicYMax; + u8 OBJMosaicY, OBJMosaicYMax; u16 BlendCnt; u16 BlendAlpha; @@ -123,8 +125,8 @@ private: void InterleaveSprites(u32* buf, u32 prio, u32* dst); void DrawSprites(u32 line, u32* dst); void DrawSpritesWindow(u32 line, u8* dst); - template void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, u32 ypos, u32* dst); - template void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, u32 ypos, u32* dst); + template void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos, u32* dst); + template void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* dst); void DoCapture(u32 line, u32 width, u32* src);