diff options
| -rw-r--r-- | melonDS.cbp | 24 | ||||
| -rw-r--r-- | src/GPU2D.cpp | 767 | ||||
| -rw-r--r-- | src/GPU2D.h | 19 | ||||
| -rw-r--r-- | src/libui_sdl/MelonCap.cpp | 347 | ||||
| -rw-r--r-- | src/libui_sdl/MelonCap.h | 34 | ||||
| -rw-r--r-- | src/libui_sdl/main.cpp | 16 | 
6 files changed, 831 insertions, 376 deletions
diff --git a/melonDS.cbp b/melonDS.cbp index be1f30b..1f51dc9 100644 --- a/melonDS.cbp +++ b/melonDS.cbp @@ -53,6 +53,24 @@  					<Add option="-m64" />  				</Linker>  			</Target> +			<Target title="DebugFast-Cap Windows"> +				<Option platforms="Windows;" /> +				<Option output="bin/DebugFast_Cap/melonDS" prefix_auto="1" extension_auto="1" /> +				<Option object_output="obj/DebugFast_Cap/" /> +				<Option type="1" /> +				<Option compiler="gcc" /> +				<Compiler> +					<Add option="-O3" /> +					<Add option="-m64" /> +					<Add option="-D_FILE_OFFSET_BITS=64" /> +					<Add option="-DMELONCAP" /> +				</Compiler> +				<Linker> +					<Add option="-m64" /> +					<Add library="winusb" /> +					<Add library="setupapi" /> +				</Linker> +			</Target>  		</Build>  		<Compiler>  			<Add option="-Wall" /> @@ -161,6 +179,12 @@  		<Unit filename="src/libui_sdl/LAN_PCap.h" />  		<Unit filename="src/libui_sdl/LAN_Socket.cpp" />  		<Unit filename="src/libui_sdl/LAN_Socket.h" /> +		<Unit filename="src/libui_sdl/MelonCap.cpp"> +			<Option target="DebugFast-Cap Windows" /> +		</Unit> +		<Unit filename="src/libui_sdl/MelonCap.h"> +			<Option target="DebugFast-Cap Windows" /> +		</Unit>  		<Unit filename="src/libui_sdl/OSD.cpp" />  		<Unit filename="src/libui_sdl/OSD.h" />  		<Unit filename="src/libui_sdl/Platform.cpp" /> diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 288ee58..b510767 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -74,7 +74,9 @@  // 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 +//   after the end of the current mosaic row (when Y counter needs reloaded) +// * for rotscaled sprites: coordinates that are inside the sprite are clamped to the sprite region +//   after being transformed for mosaic  // TODO: find which parts of DISPCNT are latched. for example, not possible to change video mode midframe. @@ -82,6 +84,16 @@  GPU2D::GPU2D(u32 num)  {      Num = num; + +    // initialize mosaic table +    for (int m = 0; m < 16; m++) +    { +        for (int x = 0; x < 256; x++) +        { +            int offset = x % (m+1); +            MosaicTable[m][x] = offset; +        } +    }  }  GPU2D::~GPU2D() @@ -118,6 +130,8 @@ void GPU2D::Reset()      BGMosaicYMax = 0;      OBJMosaicY = 0;      OBJMosaicYMax = 0; +    CurBGXMosaicTable = MosaicTable[0]; +    CurOBJXMosaicTable = MosaicTable[0];      BlendCnt = 0;      EVA = 16; @@ -207,6 +221,9 @@ void GPU2D::DoSavestate(Savestate* file)          BGExtPalStatus[2] = 0;          BGExtPalStatus[3] = 0;          OBJExtPalStatus = 0; + +        CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]]; +        CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];      }  } @@ -365,10 +382,12 @@ void GPU2D::Write8(u32 addr, u8 val)      case 0x04C:          BGMosaicSize[0] = val & 0xF;          BGMosaicSize[1] = val >> 4; +        CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]];          return;      case 0x04D:          OBJMosaicSize[0] = val & 0xF;          OBJMosaicSize[1] = val >> 4; +        CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];          return;      case 0x050: BlendCnt = (BlendCnt & 0x3F00) | val; return; @@ -497,8 +516,10 @@ void GPU2D::Write16(u32 addr, u16 val)      case 0x04C:          BGMosaicSize[0] = val & 0xF;          BGMosaicSize[1] = (val >> 4) & 0xF; +        CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]];          OBJMosaicSize[0] = (val >> 8) & 0xF;          OBJMosaicSize[1] = val >> 12; +        CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];          return;      case 0x050: BlendCnt = val & 0x3FFF; return; @@ -710,6 +731,24 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2)  } +void GPU2D::UpdateMosaicCounters(u32 line) +{ +    // Y mosaic uses incrementing 4-bit counters +    // the transformed Y position is updated every time the counter matches the MOSAIC register + +    if (OBJMosaicYCount == OBJMosaicSize[1]) +    { +        OBJMosaicYCount = 0; +        OBJMosaicY = line + 1; +    } +    else +    { +        OBJMosaicYCount++; +        OBJMosaicYCount &= 0xF; +    } +} + +  void GPU2D::DrawScanline(u32 line)  {      int stride = Accelerated ? (256*3 + 1) : 256; @@ -756,6 +795,7 @@ void GPU2D::DrawScanline(u32 line)      // always render regular graphics      DrawScanline_BGOBJ(line); +    UpdateMosaicCounters(line);      switch (dispmode)      { @@ -901,8 +941,10 @@ void GPU2D::VBlankEnd()      BGMosaicY = 0;      BGMosaicYMax = BGMosaicSize[1]; +    //OBJMosaicY = 0; +    //OBJMosaicYMax = OBJMosaicSize[1];      OBJMosaicY = 0; -    OBJMosaicYMax = OBJMosaicSize[1]; +    OBJMosaicYCount = 0;      if (Accelerated)      { @@ -1193,33 +1235,33 @@ u16* GPU2D::GetBGExtPal(u32 slot, u32 pal)      return dst;  } -u16* GPU2D::GetOBJExtPal(u32 pal) +u16* GPU2D::GetOBJExtPal()  { -    u16* dst = &OBJExtPalCache[pal << 8]; +    u16* dst = OBJExtPalCache; -    if (!(OBJExtPalStatus & (1<<pal))) +    if (!OBJExtPalStatus)      {          if (Num)          {              if (GPU::VRAMMap_BOBJExtPal & (1<<8)) -                memcpy(dst, &GPU::VRAM_I[(pal << 9)], 256*2); +                memcpy(dst, &GPU::VRAM_I[0], 16*256*2);              else -                memset(dst, 0, 256*2); +                memset(dst, 0, 16*256*2);          }          else          { -            memset(dst, 0, 256*2); +            memset(dst, 0, 16*256*2);              if (GPU::VRAMMap_AOBJExtPal & (1<<5)) -                for (int i = 0; i < 256; i+=2) -                    *(u32*)&dst[i] |= *(u32*)&GPU::VRAM_F[(pal << 9) + (i << 1)]; +                for (int i = 0; i < 16*256; i+=2) +                    *(u32*)&dst[i] |= *(u32*)&GPU::VRAM_F[i << 1];              if (GPU::VRAMMap_AOBJExtPal & (1<<6)) -                for (int i = 0; i < 256; i+=2) -                    *(u32*)&dst[i] |= *(u32*)&GPU::VRAM_G[(pal << 9) + (i << 1)]; +                for (int i = 0; i < 16*256; i+=2) +                    *(u32*)&dst[i] |= *(u32*)&GPU::VRAM_G[i << 1];          } -        OBJExtPalStatus |= (1<<pal); +        OBJExtPalStatus = 1;      }      return dst; @@ -1282,6 +1324,12 @@ void GPU2D::CalculateWindowMask(u32 line)  } +#define DoDrawBG(type, line, num) \ +    { if ((BGCnt[num] & 0x0040) && (BGMosaicSize[0] > 0)) DrawBG_##type<true>(line, num); else DrawBG_##type<false>(line, num); } + +#define DoDrawBG_Large(line) \ +    { if ((BGCnt[2] & 0x0040) && (BGMosaicSize[0] > 0)) DrawBG_Large<true>(line); else DrawBG_Large<false>(line); } +  template<u32 bgmode>  void GPU2D::DrawScanlineBGMode(u32 line)  { @@ -1292,11 +1340,11 @@ void GPU2D::DrawScanlineBGMode(u32 line)              if (DispCnt & 0x0800)              {                  if (bgmode >= 3) -                    DrawBG_Extended(line, 3); +                    DoDrawBG(Extended, line, 3)                  else if (bgmode >= 1) -                    DrawBG_Affine(line, 3); +                    DoDrawBG(Affine, line, 3)                  else -                    DrawBG_Text(line, 3); +                    DoDrawBG(Text, line, 3)              }          }          if ((BGCnt[2] & 0x3) == i) @@ -1304,18 +1352,18 @@ void GPU2D::DrawScanlineBGMode(u32 line)              if (DispCnt & 0x0400)              {                  if (bgmode == 5) -                    DrawBG_Extended(line, 2); +                    DoDrawBG(Extended, line, 2)                  else if (bgmode == 4 || bgmode == 2) -                    DrawBG_Affine(line, 2); +                    DoDrawBG(Affine, line, 2)                  else -                    DrawBG_Text(line, 2); +                    DoDrawBG(Text, line, 2)              }          }          if ((BGCnt[1] & 0x3) == i)          {              if (DispCnt & 0x0200)              { -                DrawBG_Text(line, 1); +                DoDrawBG(Text, line, 1)              }          }          if ((BGCnt[0] & 0x3) == i) @@ -1325,11 +1373,11 @@ void GPU2D::DrawScanlineBGMode(u32 line)                  if ((!Num) && (DispCnt & 0x8))                      DrawBG_3D();                  else -                    DrawBG_Text(line, 0); +                    DoDrawBG(Text, line, 0)              }          }          if ((DispCnt & 0x1000) && NumSprites) -            InterleaveSprites(0x8000 | (i<<16)); +            InterleaveSprites(0x40000 | (i<<16));      }  } @@ -1341,7 +1389,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line)          {              if (DispCnt & 0x0400)              { -                DrawBG_Large(line); +                DoDrawBG_Large(line)              }          }          if ((BGCnt[0] & 0x3) == i) @@ -1353,7 +1401,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line)              }          }          if ((DispCnt & 0x1000) && NumSprites) -            InterleaveSprites(0x8000 | (i<<16)); +            InterleaveSprites(0x40000 | (i<<16));      }  } @@ -1367,7 +1415,7 @@ void GPU2D::DrawScanlineBGMode7(u32 line)          {              if (DispCnt & 0x0200)              { -                DrawBG_Text(line, 1); +                DoDrawBG(Text, line, 1)              }          }          if ((BGCnt[0] & 0x3) == i) @@ -1377,11 +1425,11 @@ void GPU2D::DrawScanlineBGMode7(u32 line)                  if ((!Num) && (DispCnt & 0x8))                      DrawBG_3D();                  else -                    DrawBG_Text(line, 0); +                    DoDrawBG(Text, line, 0)              }          }          if ((DispCnt & 0x1000) && NumSprites) -            InterleaveSprites(0x8000 | (i<<16)); +            InterleaveSprites(0x40000 | (i<<16));      }  } @@ -1417,6 +1465,8 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)      else          memset(WindowMask, 0xFF, 256); +    ApplySpriteMosaicX(); +      switch (DispCnt & 0x7)      {      case 0: DrawScanlineBGMode<0>(line); break; @@ -1542,13 +1592,13 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)      else          BGMosaicY++; -    if (OBJMosaicY >= OBJMosaicYMax) +    /*if (OBJMosaicY >= OBJMosaicYMax)      {          OBJMosaicY = 0;          OBJMosaicYMax = OBJMosaicSize[1];      }      else -        OBJMosaicY++; +        OBJMosaicY++;*/  } @@ -1557,6 +1607,7 @@ void GPU2D::DrawPixel_Normal(u32* dst, u16 color, u32 flag)      u8 r = (color & 0x001F) << 1;      u8 g = (color & 0x03E0) >> 4;      u8 b = (color & 0x7C00) >> 9; +    //g |= ((color & 0x8000) >> 15);      *(dst+256) = *dst;      *dst = r | (g << 8) | (b << 16) | flag; @@ -1618,10 +1669,10 @@ void GPU2D::DrawBG_3D()      }  } +template<bool mosaic>  void GPU2D::DrawBG_Text(u32 line, u32 bgnum)  {      u16 bgcnt = BGCnt[bgnum]; -    u32 xmos = 0, xmossize = 0;      u32 tilesetaddr, tilemapaddr;      u16* pal; @@ -1632,9 +1683,8 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)      if (bgcnt & 0x0040)      { -        // mosaic +        // vertical mosaic          yoff -= BGMosaicY; -        xmossize = BGMosaicSize[0];      }      u32 widexmask = (bgcnt & 0x4000) ? 0x100 : 0; @@ -1671,15 +1721,15 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)      u16* curpal;      u32 pixelsaddr;      u8 color; +    u32 lastxpos;      if (bgcnt & 0x0080)      {          // 256-color          // preload shit as needed -        if (xoff & 0x7) +        if ((xoff & 0x7) || mosaic)          { -            // load a new tile              curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));              if (extpal) curpal = GetBGExtPal(extpalslot, curtile>>12); @@ -1689,31 +1739,34 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)                                       + (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 3);          } +        if (mosaic) lastxpos = xoff; +          for (int i = 0; i < 256; i++)          { -            if (!(xoff & 0x7)) +            u32 xpos; +            if (mosaic) xpos = xoff - CurBGXMosaicTable[i]; +            else        xpos = xoff; + +            if ((!mosaic && (!(xpos & 0x7))) || +                (mosaic && ((xpos >> 3) != (lastxpos >> 3))))              {                  // load a new tile -                curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3)); +                curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xpos & 0xF8) >> 2) + ((xpos & widexmask) << 3));                  if (extpal) curpal = GetBGExtPal(extpalslot, curtile>>12);                  else        curpal = pal;                  pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 6)                                           + (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 3); + +                if (mosaic) lastxpos = xpos;              }              // draw pixel              if (WindowMask[i] & (1<<bgnum))              { -                if (xmos == 0) -                { -                    u32 tilexoff = (curtile & 0x0400) ? (7-(xoff&0x7)) : (xoff&0x7); -                    color = GPU::ReadVRAM_BG<u8>(pixelsaddr + tilexoff); -                    xmos = xmossize; -                } -                else -                    xmos--; +                u32 tilexoff = (curtile & 0x0400) ? (7-(xpos&0x7)) : (xpos&0x7); +                color = GPU::ReadVRAM_BG<u8>(pixelsaddr + tilexoff);                  if (color)                      DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<<bgnum); @@ -1727,45 +1780,46 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)          // 16-color          // preload shit as needed -        if (xoff & 0x7) +        if ((xoff & 0x7) || mosaic)          { -            // load a new tile              curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));              curpal = pal + ((curtile & 0xF000) >> 8);              pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 5)                                       + (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 2);          } +        if (mosaic) lastxpos = xoff; +          for (int i = 0; i < 256; i++)          { -            if (!(xoff & 0x7)) +            u32 xpos; +            if (mosaic) xpos = xoff - CurBGXMosaicTable[i]; +            else        xpos = xoff; + +            if ((!mosaic && (!(xpos & 0x7))) || +                (mosaic && ((xpos >> 3) != (lastxpos >> 3))))              {                  // load a new tile -                curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3)); +                curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + ((xpos & 0xF8) >> 2) + ((xpos & widexmask) << 3));                  curpal = pal + ((curtile & 0xF000) >> 8);                  pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 5)                                           + (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 2); + +                if (mosaic) lastxpos = xpos;              }              // draw pixel -            // TODO: optimize VRAM access              if (WindowMask[i] & (1<<bgnum))              { -                if (xmos == 0) +                u32 tilexoff = (curtile & 0x0400) ? (7-(xpos&0x7)) : (xpos&0x7); +                if (tilexoff & 0x1)                  { -                    u32 tilexoff = (curtile & 0x0400) ? (7-(xoff&0x7)) : (xoff&0x7); -                    if (tilexoff & 0x1) -                    { -                        color = GPU::ReadVRAM_BG<u8>(pixelsaddr + (tilexoff >> 1)) >> 4; -                    } -                    else -                    { -                        color = GPU::ReadVRAM_BG<u8>(pixelsaddr + (tilexoff >> 1)) & 0x0F; -                    } -                    xmos = xmossize; +                    color = GPU::ReadVRAM_BG<u8>(pixelsaddr + (tilexoff >> 1)) >> 4;                  }                  else -                    xmos--; +                { +                    color = GPU::ReadVRAM_BG<u8>(pixelsaddr + (tilexoff >> 1)) & 0x0F; +                }                  if (color)                      DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<<bgnum); @@ -1776,10 +1830,10 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)      }  } +template<bool mosaic>  void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)  {      u16 bgcnt = BGCnt[bgnum]; -    u32 xmos = 0, xmossize = 0;      u32 tilesetaddr, tilemapaddr;      u16* pal; @@ -1808,10 +1862,9 @@ void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)      if (bgcnt & 0x0040)      { -        // mosaic +        // vertical mosaic          rotX -= (BGMosaicY * rotB);          rotY -= (BGMosaicY * rotD); -        xmossize = BGMosaicSize[0];      }      if (Num) @@ -1838,28 +1891,31 @@ void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)      {          if (WindowMask[i] & (1<<bgnum))          { -            if (xmos > 0) +            s32 finalX, finalY; +            if (mosaic)              { -                if (color) -                    DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<bgnum); - -                xmos--; +                int im = CurBGXMosaicTable[i]; +                finalX = rotX - (im * rotA); +                finalY = rotY - (im * rotC);              }              else -            if ((!((rotX|rotY) & overflowmask)))              { -                curtile = GPU::ReadVRAM_BG<u8>(tilemapaddr + ((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11))); +                finalX = rotX; +                finalY = rotY; +            } + +            if ((!((finalX|finalY) & overflowmask))) +            { +                curtile = GPU::ReadVRAM_BG<u8>(tilemapaddr + ((((finalY & coordmask) >> 11) << yshift) + ((finalX & coordmask) >> 11)));                  // draw pixel -                u32 tilexoff = (rotX >> 8) & 0x7; -                u32 tileyoff = (rotY >> 8) & 0x7; +                u32 tilexoff = (finalX >> 8) & 0x7; +                u32 tileyoff = (finalY >> 8) & 0x7;                  color = GPU::ReadVRAM_BG<u8>(tilesetaddr + (curtile << 6) + (tileyoff << 3) + tilexoff);                  if (color)                      DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<bgnum); - -                xmos = xmossize;              }          } @@ -1871,10 +1927,10 @@ void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)      BGYRefInternal[bgnum-2] += rotD;  } +template<bool mosaic>  void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)  {      u16 bgcnt = BGCnt[bgnum]; -    u32 xmos = 0, xmossize = 0;      u32 tilesetaddr, tilemapaddr;      u16* pal; @@ -1892,10 +1948,9 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)      if (bgcnt & 0x0040)      { -        // mosaic +        // vertical mosaic          rotX -= (BGMosaicY * rotB);          rotY -= (BGMosaicY * rotD); -        xmossize = BGMosaicSize[0];      }      if (bgcnt & 0x0080) @@ -1937,22 +1992,25 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)              {                  if (WindowMask[i] & (1<<bgnum))                  { -                    if (xmos > 0) +                    s32 finalX, finalY; +                    if (mosaic)                      { -                        if (color & 0x8000) -                            DrawPixel(&BGOBJLine[i], color, 0x01000000<<bgnum); - -                        xmos--; +                        int im = CurBGXMosaicTable[i]; +                        finalX = rotX - (im * rotA); +                        finalY = rotY - (im * rotC);                      }                      else -                    if (!(rotX & ofxmask) && !(rotY & ofymask))                      { -                        color = GPU::ReadVRAM_BG<u16>(tilemapaddr + (((((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)) << 1)); +                        finalX = rotX; +                        finalY = rotY; +                    } + +                    if (!(finalX & ofxmask) && !(finalY & ofymask)) +                    { +                        color = GPU::ReadVRAM_BG<u16>(tilemapaddr + (((((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8)) << 1));                          if (color & 0x8000)                              DrawPixel(&BGOBJLine[i], color, 0x01000000<<bgnum); - -                        xmos = xmossize;                      }                  } @@ -1973,22 +2031,25 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)              {                  if (WindowMask[i] & (1<<bgnum))                  { -                    if (xmos > 0) +                    s32 finalX, finalY; +                    if (mosaic)                      { -                        if (color) -                            DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<bgnum); - -                        xmos--; +                        int im = CurBGXMosaicTable[i]; +                        finalX = rotX - (im * rotA); +                        finalY = rotY - (im * rotC);                      }                      else -                    if (!(rotX & ofxmask) && !(rotY & ofymask))                      { -                        color = GPU::ReadVRAM_BG<u8>(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); +                        finalX = rotX; +                        finalY = rotY; +                    } + +                    if (!(finalX & ofxmask) && !(finalY & ofymask)) +                    { +                        color = GPU::ReadVRAM_BG<u8>(tilemapaddr + (((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8));                          if (color)                              DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<bgnum); - -                        xmos = xmossize;                      }                  } @@ -2040,24 +2101,29 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)          {              if (WindowMask[i] & (1<<bgnum))              { -                if (xmos > 0) +                s32 finalX, finalY; +                if (mosaic)                  { -                    if (color) -                        DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<<bgnum); - -                    xmos--; +                    int im = CurBGXMosaicTable[i]; +                    finalX = rotX - (im * rotA); +                    finalY = rotY - (im * rotC);                  }                  else -                if ((!((rotX|rotY) & overflowmask)))                  { -                    curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + (((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11)) << 1)); +                    finalX = rotX; +                    finalY = rotY; +                } + +                if ((!((finalX|finalY) & overflowmask))) +                { +                    curtile = GPU::ReadVRAM_BG<u16>(tilemapaddr + (((((finalY & coordmask) >> 11) << yshift) + ((finalX & coordmask) >> 11)) << 1));                      if (extpal) curpal = GetBGExtPal(bgnum, curtile>>12);                      else        curpal = pal;                      // draw pixel -                    u32 tilexoff = (rotX >> 8) & 0x7; -                    u32 tileyoff = (rotY >> 8) & 0x7; +                    u32 tilexoff = (finalX >> 8) & 0x7; +                    u32 tileyoff = (finalY >> 8) & 0x7;                      if (curtile & 0x0400) tilexoff = 7-tilexoff;                      if (curtile & 0x0800) tileyoff = 7-tileyoff; @@ -2066,8 +2132,6 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)                      if (color)                          DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<<bgnum); - -                    xmos = xmossize;                  }              } @@ -2080,10 +2144,10 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)      BGYRefInternal[bgnum-2] += rotD;  } +template<bool mosaic>  void GPU2D::DrawBG_Large(u32 line) // BG is always BG2  {      u16 bgcnt = BGCnt[2]; -    u32 xmos = 0, xmossize = 0;      u32 tilesetaddr, tilemapaddr;      u16* pal; @@ -2125,10 +2189,9 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2      if (bgcnt & 0x0040)      { -        // mosaic +        // vertical mosaic          rotX -= (BGMosaicY * rotB);          rotY -= (BGMosaicY * rotD); -        xmossize = BGMosaicSize[0];      }      if (Num) tilemapaddr = 0x06200000; @@ -2145,17 +2208,22 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2      {          if (WindowMask[i] & (1<<2))          { -            if (xmos > 0) +            s32 finalX, finalY; +            if (mosaic)              { -                if (color) -                    DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<2); - -                xmos--; +                int im = CurBGXMosaicTable[i]; +                finalX = rotX - (im * rotA); +                finalY = rotY - (im * rotC);              }              else -            if (!(rotX & ofxmask) && !(rotY & ofymask))              { -                color = GPU::ReadVRAM_BG<u8>(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)); +                finalX = rotX; +                finalY = rotY; +            } + +            if (!(finalX & ofxmask) && !(finalY & ofymask)) +            { +                color = GPU::ReadVRAM_BG<u8>(tilemapaddr + (((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8));                  if (color)                      DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<2); @@ -2170,17 +2238,88 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2      BGYRefInternal[0] += rotD;  } +void GPU2D::ApplySpriteMosaicX() +{ +    // apply X mosaic if needed +    // X mosaic for sprites is applied after all sprites are rendered + +    if (OBJMosaicSize[0] == 0) return; + +    u32 lastcolor = OBJLine[0]; + +    for (u32 i = 1; i < 256; i++) +    { +        if (!(OBJLine[i] & 0x100000)) +        { +            // not a mosaic'd sprite pixel +            continue; +        } + +        if ((!(OBJLine[i-1] & 0x100000)) || (CurOBJXMosaicTable[i] == 0)) +            lastcolor = OBJLine[i]; +        else +            OBJLine[i] = lastcolor; +    } +} +  void GPU2D::InterleaveSprites(u32 prio)  { -    for (u32 i = 0; i < 256; i++) +    u16* pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; + +    if (DispCnt & 0x80000000) +    { +        u16* extpal = GetOBJExtPal(); + +        for (u32 i = 0; i < 256; i++) +        { +            if ((OBJLine[i] & 0x70000) != prio) continue; +            if (!(WindowMask[i] & 0x10))        continue; + +            u16 color; +            u32 pixel = OBJLine[i]; + +            if (pixel & 0x8000) +                color = pixel & 0x7FFF; +            else if (pixel & 0x1000) +                color = pal[pixel & 0xFF]; +            else +                color = extpal[pixel & 0xFFF]; + +            DrawPixel(&BGOBJLine[i], color, pixel & 0xFF000000); +        } +    } +    else      { -        if (((OBJLine[i] & 0xF8000) == prio) && (WindowMask[i] & 0x10)) +        // optimized no-extpal version + +        for (u32 i = 0; i < 256; i++)          { -            DrawPixel(&BGOBJLine[i], OBJLine[i] & 0x7FFF, OBJLine[i] & 0xFF000000); +            if ((OBJLine[i] & 0x70000) != prio) continue; +            if (!(WindowMask[i] & 0x10))        continue; + +            u16 color; +            u32 pixel = OBJLine[i]; + +            if (pixel & 0x8000) +                color = pixel & 0x7FFF; +            else +                color = pal[pixel & 0xFF]; + +            DrawPixel(&BGOBJLine[i], color, pixel & 0xFF000000);          }      }  } +#define DoDrawSprite(type, ...) \ +    if (iswin) \ +    { \ +        DrawSprite_##type<true>(__VA_ARGS__); \ +    } \ +    else \ +    { \ +        DrawSprite_##type<false>(__VA_ARGS__); \ +    } +  void GPU2D::DrawSprites(u32 line)  {      NumSprites = 0; @@ -2241,10 +2380,7 @@ void GPU2D::DrawSprites(u32 line)                  u32 rotparamgroup = (attrib[1] >> 9) & 0x1F; -                if (iswin) -                    DrawSprite_Rotscale<true>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos); -                else -                    DrawSprite_Rotscale<false>(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos); +                DoDrawSprite(Rotscale, attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);                  NumSprites++;              } @@ -2270,10 +2406,7 @@ void GPU2D::DrawSprites(u32 line)                  if (attrib[1] & 0x2000)                      ypos = height-1 - ypos; -                if (iswin) -                    DrawSprite_Normal<true>(attrib, width, xpos, ypos); -                else -                    DrawSprite_Normal<false>(attrib, width, xpos, ypos); +                DoDrawSprite(Normal, attrib, width, xpos, ypos);                  NumSprites++;              } @@ -2284,25 +2417,22 @@ void GPU2D::DrawSprites(u32 line)  template<bool window>  void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos)  { -    u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000; +    u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000;      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) +    if ((attrib[0] & 0x1000) && !window)      { -        // mosaic -        ypos -= OBJMosaicY; +        // apply Y mosaic +        ypos = OBJMosaicY - (attrib[0] & 0xFF);          if (ypos < 0) ypos = 0; -        xmossize = OBJMosaicSize[0]; -        if (xpos > 0) -            xmos = (xmossize+1) - (xpos % (xmossize+1)); +        pixelattr |= 0x100000;      }      u32 xoff; @@ -2337,7 +2467,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32          if (!alpha) return;          alpha++; -        prio |= (0xC0000000 | (alpha << 24)); +        pixelattr |= (0xC0000000 | (alpha << 24));          if (DispCnt & 0x40)          { @@ -2369,31 +2499,23 @@ 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<u16>(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1));          for (; xoff < boundwidth;)          {              if ((u32)rotX < width && (u32)rotY < height)              { -                if (xmos == 0) -                { -                    color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1)); -                    xmos = xmossize; -                } -                else -                    xmos--; +                color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1));                  if (color & 0x8000)                  {                      if (window) OBJWindow[xpos] = 1; -                    else        OBJLine[xpos] = color | prio; +                    else        OBJLine[xpos] = color | pixelattr; +                } +                else if (!window) +                { +                    if (OBJLine[xpos] == 0) +                        OBJLine[xpos] = pixelattr & 0x180000;                  } -            } -            else -            { -                if (xmos == 0) xmos = xmossize; -                else           xmos--;              }              rotX += rotA; @@ -2414,8 +2536,8 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32              ytilefactor = 0x20;          } -        if (spritemode == 1) prio |= 0x80000000; -        else                 prio |= 0x10000000; +        if (spritemode == 1) pixelattr |= 0x80000000; +        else                 pixelattr |= 0x10000000;          if (attrib[0] & 0x2000)          { @@ -2424,40 +2546,30 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32              ytilefactor <<= 5;              u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum; -            u32 extpal = (DispCnt & 0x80000000); - -            u16* pal;              if (!window)              { -                if (extpal) pal = GetOBJExtPal(attrib[2] >> 12); -                else        pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; +                if (!(DispCnt & 0x80000000)) +                    pixelattr |= 0x1000; +                else +                    pixelattr |= ((attrib[2] & 0xF000) >> 4);              } -            if (xmos && !(attrib[0]&0x0200)) -                color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8)); -              for (; xoff < boundwidth;)              {                  if ((u32)rotX < width && (u32)rotY < height)                  { -                    if (xmos == 0) -                    { -                        color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8)); -                        xmos = xmossize; -                    } -                    else -                        xmos--; +                    color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8));                      if (color)                      {                          if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; +                        else        OBJLine[xpos] = color | pixelattr; +                    } +                    else if (!window) +                    { +                        if (OBJLine[xpos] == 0) +                            OBJLine[xpos] = pixelattr & 0x180000;                      } -                } -                else -                { -                    if (xmos == 0) xmos = xmossize; -                    else           xmos--;                  }                  rotX += rotA; @@ -2473,49 +2585,32 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32              ytilefactor <<= 5;              u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum; -            u16* pal;              if (!window)              { -                pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; -                pal += (attrib[2] & 0xF000) >> 8; -            } - -            if (xmos && !(attrib[0]&0x0200)) -            { -                color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); -                if (rotX & 0x100) -                    color >>= 4; -                else -                    color &= 0x0F; +                pixelattr |= 0x1000; +                pixelattr |= ((attrib[2] & 0xF000) >> 8);              }              for (; xoff < boundwidth;)              {                  if ((u32)rotX < width && (u32)rotY < height)                  { -                    if (xmos == 0) -                    { -                        color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); -                        if (rotX & 0x100) -                            color >>= 4; -                        else -                            color &= 0x0F; - -                        xmos = xmossize; -                    } +                    color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9)); +                    if (rotX & 0x100) +                        color >>= 4;                      else -                        xmos--; +                        color &= 0x0F;                      if (color)                      {                          if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; +                        else        OBJLine[xpos] = color | pixelattr; +                    } +                    else if (!window) +                    { +                        if (OBJLine[xpos] == 0) +                            OBJLine[xpos] = pixelattr & 0x180000;                      } -                } -                else -                { -                    if (xmos == 0) xmos = xmossize; -                    else           xmos--;                  }                  rotX += rotA; @@ -2530,22 +2625,19 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32  template<bool window>  void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)  { -    u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000; +    u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000;      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) +    if ((attrib[0] & 0x1000) && !window)      { -        // mosaic -        ypos -= OBJMosaicY; +        // apply Y mosaic +        ypos = OBJMosaicY - (attrib[0] & 0xFF);          if (ypos < 0) ypos = 0; -        xmossize = OBJMosaicSize[0]; -        if (xpos > 0) -            xmos = (xmossize+1) - (xpos % (xmossize+1)); +        pixelattr |= 0x100000;      }      u32 xoff; @@ -2572,7 +2664,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)          if (!alpha) return;          alpha++; -        prio |= (0xC0000000 | (alpha << 24)); +        pixelattr |= (0xC0000000 | (alpha << 24));          if (DispCnt & 0x40)          { @@ -2604,60 +2696,39 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)          }          u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum; +        s32 pixelstride; -        if (attrib[1] & 0x1000) +        if (attrib[1] & 0x1000) // xflip          { -            pixelsaddr += ((width-1 - xoff) << 1); -            if (xmos) color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr); - -            for (; xoff < xend;) -            { -                if (xmos == 0) -                { -                    color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr); -                    xmos = xmossize; -                } -                else -                    xmos--; - -                pixelsaddr -= 2; - -                if (color & 0x8000) -                { -                    if (window) OBJWindow[xpos] = 1; -                    else        OBJLine[xpos] = color | prio; -                } - -                xoff++; -                xpos++; -            } +            pixelsaddr += (width-1 << 1); +            pixelsaddr -= (xoff << 1); +            pixelstride = -2;          }          else          {              pixelsaddr += (xoff << 1); -            if (xmos) color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr); - -            for (; xoff < xend;) -            { -                if (xmos == 0) -                { -                    color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr); -                    xmos = xmossize; -                } -                else -                    xmos--; +            pixelstride = 2; +        } -                pixelsaddr += 2; +        for (; xoff < xend;) +        { +            color = GPU::ReadVRAM_OBJ<u16>(pixelsaddr); -                if (color & 0x8000) -                { -                    if (window) OBJWindow[xpos] = 1; -                    else        OBJLine[xpos] = color | prio; -                } +            pixelsaddr += pixelstride; -                xoff++; -                xpos++; +            if (color & 0x8000) +            { +                if (window) OBJWindow[xpos] = 1; +                else        OBJLine[xpos] = color | pixelattr; +            } +            else if (!window) +            { +                if (OBJLine[xpos] == 0) +                    OBJLine[xpos] = pixelattr & 0x180000;              } + +            xoff++; +            xpos++;          }      }      else @@ -2672,8 +2743,8 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)              tilenum += ((ypos >> 3) * 0x20);          } -        if (spritemode == 1) prio |= 0x80000000; -        else                 prio |= 0x10000000; +        if (spritemode == 1) pixelattr |= 0x80000000; +        else                 pixelattr |= 0x10000000;          if (attrib[0] & 0x2000)          { @@ -2681,73 +2752,51 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)              tilenum <<= 5;              u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;              pixelsaddr += ((ypos & 0x7) << 3); +            s32 pixelstride; -            u32 extpal = (DispCnt & 0x80000000); - -            u16* pal;              if (!window)              { -                if (extpal) pal = GetOBJExtPal(attrib[2] >> 12); -                else        pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; +                if (!(DispCnt & 0x80000000)) +                    pixelattr |= 0x1000; +                else +                    pixelattr |= ((attrib[2] & 0xF000) >> 4);              } -            if (attrib[1] & 0x1000) // xflip. TODO: do better? oh well for now this works +            if (attrib[1] & 0x1000) // xflip              { -                pixelsaddr += (((width-1 - xoff) & wmask) << 3); -                pixelsaddr += ((width-1 - xoff) & 0x7); -                if (xmos) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr); - -                for (; xoff < xend;) -                { -                    if (xmos == 0) -                    { -                        color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr); -                        xmos = xmossize; -                    } -                    else -                        xmos--; - -                    pixelsaddr--; - -                    if (color) -                    { -                        if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; -                    } - -                    xoff++; -                    xpos++; -                    if (!(xoff & 0x7)) pixelsaddr -= 56; -                } +                pixelsaddr += (((width-1) & wmask) << 3); +                pixelsaddr += ((width-1) & 0x7); +                pixelsaddr -= ((xoff & wmask) << 3); +                pixelsaddr -= (xoff & 0x7); +                pixelstride = -1;              }              else              {                  pixelsaddr += ((xoff & wmask) << 3);                  pixelsaddr += (xoff & 0x7); -                if (xmos) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr); - -                for (; xoff < xend;) -                { -                    if (xmos == 0) -                    { -                        color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr); -                        xmos = xmossize; -                    } -                    else -                        xmos--; +                pixelstride = 1; +            } -                    pixelsaddr++; +            for (; xoff < xend;) +            { +                color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr); -                    if (color) -                    { -                        if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; -                    } +                pixelsaddr += pixelstride; -                    xoff++; -                    xpos++; -                    if (!(xoff & 0x7)) pixelsaddr += 56; +                if (color) +                { +                    if (window) OBJWindow[xpos] = 1; +                    else        OBJLine[xpos] = color | pixelattr; +                } +                else if (!window) +                { +                    if (OBJLine[xpos] == 0) +                        OBJLine[xpos] = pixelattr & 0x180000;                  } + +                xoff++; +                xpos++; +                if (!(xoff & 0x7)) pixelsaddr += (56 * pixelstride);              }          }          else @@ -2756,81 +2805,59 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)              tilenum <<= 5;              u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;              pixelsaddr += ((ypos & 0x7) << 2); +            s32 pixelstride; -            u16* pal;              if (!window)              { -                pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200]; -                pal += (attrib[2] & 0xF000) >> 8; +                pixelattr |= 0x1000; +                pixelattr |= ((attrib[2] & 0xF000) >> 8);              } -            if (attrib[1] & 0x1000) // xflip. TODO: do better? oh well for now this works -            { -                pixelsaddr += (((width-1 - xoff) & wmask) << 2); -                pixelsaddr += (((width-1 - xoff) & 0x7) >> 1); -                if (xmos) -                { -                    if (xoff & 0x1) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; -                    else            color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4; -                } - -                for (; xoff < xend;) -                { -                    if (xmos == 0) -                    { -                        if (xoff & 0x1) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; -                        else            color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4; -                        xmos = xmossize; -                    } -                    else -                        xmos--; - -                    if (xoff & 0x1) pixelsaddr--; +            // TODO: optimize VRAM access!! +            // TODO: do xflip better? the 'two pixels per byte' thing makes it a bit shitty -                    if (color) -                    { -                        if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; -                    } - -                    xoff++; -                    xpos++; -                    if (!(xoff & 0x7)) pixelsaddr -= 28; -                } +            if (attrib[1] & 0x1000) // xflip +            { +                pixelsaddr += (((width-1) & wmask) << 2); +                pixelsaddr += (((width-1) & 0x7) >> 1); +                pixelsaddr -= ((xoff & wmask) << 2); +                pixelsaddr -= ((xoff & 0x7) >> 1); +                pixelstride = -1;              }              else              {                  pixelsaddr += ((xoff & wmask) << 2);                  pixelsaddr += ((xoff & 0x7) >> 1); -                if (xmos) +                pixelstride = 1; +            } + +            for (; xoff < xend;) +            { +                if (attrib[1] & 0x1000)                  { -                    if (xoff & 0x1) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4; -                    else            color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; +                    if (xoff & 0x1) { color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; pixelsaddr--; } +                    else              color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4;                  } - -                for (; xoff < xend;) +                else                  { -                    if (xmos == 0) -                    { -                        if (xoff & 0x1) color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4; -                        else            color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; -                        xmos = xmossize; -                    } -                    else -                        xmos--; - -                    if (xoff & 0x1) pixelsaddr++; - -                    if (color) -                    { -                        if (window) OBJWindow[xpos] = 1; -                        else        OBJLine[xpos] = pal[color] | prio; -                    } +                    if (xoff & 0x1) { color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) >> 4; pixelsaddr++; } +                    else              color = GPU::ReadVRAM_OBJ<u8>(pixelsaddr) & 0x0F; +                } -                    xoff++; -                    xpos++; -                    if (!(xoff & 0x7)) pixelsaddr += 28; +                if (color) +                { +                    if (window) OBJWindow[xpos] = 1; +                    else        OBJLine[xpos] = color | pixelattr; +                } +                else if (!window) +                { +                    if (OBJLine[xpos] == 0) +                        OBJLine[xpos] = pixelattr & 0x180000;                  } + +                xoff++; +                xpos++; +                if (!(xoff & 0x7)) pixelsaddr += ((attrib[1] & 0x1000) ? -28 : 28);              }          }      } diff --git a/src/GPU2D.h b/src/GPU2D.h index 6ad9783..8c6ec27 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -63,7 +63,7 @@ public:      void OBJExtPalDirty();      u16* GetBGExtPal(u32 slot, u32 pal); -    u16* GetOBJExtPal(u32 pal); +    u16* GetOBJExtPal();  private:      u32 Num; @@ -111,7 +111,11 @@ private:      u8 BGMosaicSize[2];      u8 OBJMosaicSize[2];      u8 BGMosaicY, BGMosaicYMax; -    u8 OBJMosaicY, OBJMosaicYMax; +    u8 OBJMosaicYCount, OBJMosaicY, OBJMosaicYMax; + +    u8 MosaicTable[16][256]; +    u8* CurBGXMosaicTable; +    u8* CurOBJXMosaicTable;      u16 BlendCnt;      u16 BlendAlpha; @@ -133,6 +137,8 @@ private:      u32 ColorBrightnessDown(u32 val, u32 factor);      u32 ColorComposite(int i, u32 val1, u32 val2); +    void UpdateMosaicCounters(u32 line); +      template<u32 bgmode> void DrawScanlineBGMode(u32 line);      void DrawScanlineBGMode6(u32 line);      void DrawScanlineBGMode7(u32 line); @@ -143,11 +149,12 @@ private:      void (*DrawPixel)(u32* dst, u16 color, u32 flag);      void DrawBG_3D(); -    void DrawBG_Text(u32 line, u32 bgnum); -    void DrawBG_Affine(u32 line, u32 bgnum); -    void DrawBG_Extended(u32 line, u32 bgnum); -    void DrawBG_Large(u32 line); +    template<bool mosaic> void DrawBG_Text(u32 line, u32 bgnum); +    template<bool mosaic> void DrawBG_Affine(u32 line, u32 bgnum); +    template<bool mosaic> void DrawBG_Extended(u32 line, u32 bgnum); +    template<bool mosaic> void DrawBG_Large(u32 line); +    void ApplySpriteMosaicX();      void InterleaveSprites(u32 prio);      template<bool window> void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos);      template<bool window> void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos); diff --git a/src/libui_sdl/MelonCap.cpp b/src/libui_sdl/MelonCap.cpp new file mode 100644 index 0000000..fde440a --- /dev/null +++ b/src/libui_sdl/MelonCap.cpp @@ -0,0 +1,347 @@ +/* +    Copyright 2016-2019 Arisotura + +    This file is part of melonDS. + +    melonDS is free software: you can redistribute it and/or modify it under +    the terms of the GNU General Public License as published by the Free +    Software Foundation, either version 3 of the License, or (at your option) +    any later version. + +    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY +    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License along +    with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdio.h> +#include <string.h> +#include "MelonCap.h" +#include "libui/ui.h" +#include "../NDS.h" +#include "../GPU.h" + +#include <windows.h> +#include <setupapi.h> +#include <guiddef.h> +#include <winusb.h> + + +namespace MelonCap +{ + +uiWindow* Window; +uiArea* Area; +uiAreaHandler AreaHandler; +uiDrawBitmap* WinBitmap; +bool WinBitmapInited; + +u32* WinBitmapData; + +// this crap was built from the reverse-engineering of ds_capture.exe +// mixed in with their Linux capture sample code + +GUID InterfaceClass = {0xA0B880F6, 0xD6A5, 0x4700, {0xA8, 0xEA, 0x22, 0x28, 0x2A, 0xCA, 0x55, 0x87}}; +HANDLE CapHandle; +WINUSB_INTERFACE_HANDLE CapUSBHandle; + + +void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params) +{ +    if (!WinBitmapInited) +    { +        if (WinBitmap) uiDrawFreeBitmap(WinBitmap); + +        WinBitmapInited = true; +        WinBitmap = uiDrawNewBitmap(params->Context, 768, 384, 0); +    } + +    if (!WinBitmap) return; +    if (!WinBitmapData) return; + +    uiRect rc = {0, 0, 768, 384}; + +    uiDrawBitmapUpdate(WinBitmap, WinBitmapData); +    uiDrawBitmapDraw(params->Context, WinBitmap, &rc, &rc, 0); +} + +void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt) +{ +} + +void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left) +{ +} + +void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area) +{ +} + +int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) +{ +    return 1; +} + +void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height) +{ +} + + +void Init() +{ +    printf("MelonCap init\n"); + +    HDEVINFO devinfo = SetupDiGetClassDevsW(&InterfaceClass, NULL, NULL, DIGCF_DEVICEINTERFACE|DIGCF_PRESENT); +    if (devinfo == INVALID_HANDLE_VALUE) return; + +    int member = 0; +    bool good = false; +    for (;;) +    { +        SP_DEVICE_INTERFACE_DATA interfacedata; +        memset(&interfacedata, 0, sizeof(interfacedata)); +        interfacedata.cbSize = sizeof(interfacedata); + +        BOOL ret = SetupDiEnumDeviceInterfaces(devinfo, NULL, &InterfaceClass, member, &interfacedata); +        if (!ret) +        { +            printf("found %d interfaces\n", member); +            break; +        } + +        DWORD requiredsize = 0; +        SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, NULL, NULL, &requiredsize, NULL); +        printf("%d: required size %d\n", member, requiredsize); + +        PSP_DEVICE_INTERFACE_DETAIL_DATA_W interfacedetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new u8[requiredsize]; +        interfacedetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_W); +        ret = SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, interfacedetail, requiredsize, NULL, NULL); +        if (ret) +        { +            printf("got interface detail: path=%S\n", interfacedetail->DevicePath); +            HANDLE file = CreateFileW(interfacedetail->DevicePath, GENERIC_READ|GENERIC_WRITE, +                                      FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, +                                      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); +            if (file != INVALID_HANDLE_VALUE) +            { +                WINUSB_INTERFACE_HANDLE usbhandle; +                ret = WinUsb_Initialize(file, &usbhandle); +                if (ret) +                { +                    int val; +                    val = 0x1E; +                    WinUsb_SetPipePolicy(usbhandle, 0x00, PIPE_TRANSFER_TIMEOUT, 4, &val); +                    val = 0x32; +                    WinUsb_SetPipePolicy(usbhandle, 0x82, PIPE_TRANSFER_TIMEOUT, 4, &val); +                    val = 0x01; +                    WinUsb_SetPipePolicy(usbhandle, 0x82, RAW_IO, 1, &val); + +                    printf("looking good\n"); +                    good = true; + +                    CapHandle = file; +                    CapUSBHandle = usbhandle; +                } +                else +                    CloseHandle(file); +            } +        } + +        delete[] (u8*)interfacedetail; + +        if (good) break; + +        member++; +    } + +    SetupDiDestroyDeviceInfoList(devinfo); + + +    AreaHandler.Draw = OnAreaDraw; +    AreaHandler.MouseEvent = OnAreaMouseEvent; +    AreaHandler.MouseCrossed = OnAreaMouseCrossed; +    AreaHandler.DragBroken = OnAreaDragBroken; +    AreaHandler.KeyEvent = OnAreaKeyEvent; +    AreaHandler.Resize = OnAreaResize; + +    WinBitmapInited = false; +    WinBitmapData = new u32[768*384]; + +    Window = uiNewWindow("melonDS - topnotch pixel checker", 768, 384, 0, 0, 0); +    Area = uiNewArea(&AreaHandler); +    uiWindowSetChild(Window, uiControl(Area)); + +    uiControlShow(uiControl(Window)); +} + +void DeInit() +{ +    uiControlDestroy(uiControl(Window)); +    uiDrawFreeBitmap(WinBitmap); +    WinBitmapInited = false; +    delete[] WinBitmapData; + +    WinUsb_Free(CapUSBHandle); +    CloseHandle(CapHandle); +} + + +int VendorIn(u8 req, u16 len, u8* buf) +{ +    WINUSB_SETUP_PACKET pkt; +    pkt.RequestType = 0xC0; // device to host +    pkt.Request = req; +    pkt.Value = 0; // ????? +    pkt.Index = 0; +    pkt.Length = len; + +    ULONG ret = 0; +    BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL); +    if (!res) return -1; +    return ret; +} + +int VendorOut(u8 req, u16 val, u16 len, u8* buf) +{ +    WINUSB_SETUP_PACKET pkt; +    pkt.RequestType = 0x40; // host to device +    pkt.Request = req; +    pkt.Value = val; +    pkt.Index = 0; +    pkt.Length = len; + +    ULONG ret = 0; +    BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL); +    if (!res) return -1; +    return ret; +} + +int BulkIn(u8* buf, u32 len) +{ +    ULONG ret = 0; +    BOOL res = WinUsb_ReadPipe(CapUSBHandle, 0x82, buf, len, &ret, NULL); +    if (!res) return -1; +    return ret; +} + + +u32 ConvertColor(u16 col) +{ +    u32 b = col & 0x001F; +    u32 g = (col & 0x07E0) >> 5; +    u32 r = (col & 0xF800) >> 11; + +    u32 ret = 0xFF000000; +    ret |= ((r << 3) | (r >> 2)) << 16; +    ret |= ((g << 2) | (g >> 4)) << 8; +    ret |= (b << 3) | (b >> 2); +    return ret; +} + +void CaptureFrame() +{ +    u32 ret; +    u8 derp; +    u32 framelen = 256*384*2; +    u16 frame[framelen/2]; +    u32 framepos = 0; +    u8 frameinfo[64]; + +    ret = VendorOut(0x30, 0, 0, &derp); +    if (ret < 0) return; + +    int tries = 0; +    while (framepos < framelen) +    { +        ret = BulkIn((u8*)&frame[framepos/2], framelen-framepos); +        if (ret < 0) break; +        if (ret == 0) +        { +            tries++; +            if (tries >= 100) break; +            continue; +        } +        framepos += ret; +    } + +    ret = VendorIn(0x30, 64, frameinfo); +    if (ret < 0) return; +    if ((frameinfo[0] & 0x03) != 0x03) return; +    if (!frameinfo[52]) return; + +    u16* in = &frame[0]; +    u32* out = &WinBitmapData[256]; + +    for (int y = 0; y < 384; y++) +    { +        u32* out = &WinBitmapData[((y/2)*768) + ((y&1)*128) + 256]; + +        if (!(frameinfo[y>>3] & (1<<(y&7)))) +        { +            continue; +        } + +        for (int x = 0; x < 256/2; x++) +        { +            out[0] = ConvertColor(in[1]); +            out[768*192] = ConvertColor(in[0]); +            out++; +            in += 2; +        } +    } +} + +void Update() +{ +    // melonDS output + +    int frontbuf = GPU::FrontBuffer; + +    u32* topbuf = GPU::Framebuffer[frontbuf][0]; +    if (topbuf) +    { +        for (int y = 0; y < 192; y++) +        { +            memcpy(&WinBitmapData[y*768], &topbuf[y*256], 256*4); +        } +    } + +    u32* botbuf = GPU::Framebuffer[frontbuf][1]; +    if (botbuf) +    { +        for (int y = 0; y < 192; y++) +        { +            memcpy(&WinBitmapData[(y+192)*768], &botbuf[y*256], 256*4); +        } +    } + +    // DS capture + +    CaptureFrame(); + +    // compare + +    for (int y = 0; y < 384; y++) +    { +        for (int x = 0; x < 256; x++) +        { +            u32 colA = WinBitmapData[(y*768) + x + 0]; +            u32 colB = WinBitmapData[(y*768) + x + 256]; + +            // best we get from the capture card is RGB565 +            // so we'll ignore the lower bits +            const u32 mask = 0x00F8FCF8; +            colA &= mask; +            colB &= mask; + +            if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF00FF00; +            else              WinBitmapData[(y*768) + x + 512] = 0xFFFF0000; +        } +    } + +    uiAreaQueueRedrawAll(Area); +} + +} diff --git a/src/libui_sdl/MelonCap.h b/src/libui_sdl/MelonCap.h new file mode 100644 index 0000000..1185dea --- /dev/null +++ b/src/libui_sdl/MelonCap.h @@ -0,0 +1,34 @@ +/* +    Copyright 2016-2019 Arisotura + +    This file is part of melonDS. + +    melonDS is free software: you can redistribute it and/or modify it under +    the terms of the GNU General Public License as published by the Free +    Software Foundation, either version 3 of the License, or (at your option) +    any later version. + +    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY +    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License along +    with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONCAP_H +#define MELONCAP_H + +#include "types.h" + +namespace MelonCap +{ + +void Init(); +void DeInit(); + +void Update(); + +} + +#endif // MELONCAP_H diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index d3f9024..08d38b2 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -48,6 +48,10 @@  #include "OSD.h" +#ifdef MELONCAP +#include "MelonCap.h" +#endif // MELONCAP +  // savestate slot mapping  // 1-8: regular slots (quick access) @@ -999,6 +1003,10 @@ int EmuThreadFunc(void* burp)              // emulate              u32 nlines = NDS::RunFrame(); +#ifdef MELONCAP +            MelonCap::Update(); +#endif // MELONCAP +              if (EmuRunning == 0) break;              if (Screen_UseGL) @@ -2737,6 +2745,10 @@ int main(int argc, char** argv)      uiMenuItemSetChecked(MenuItem_AudioSync, Config::AudioSync==1);      uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1); +#ifdef MELONCAP +    MelonCap::Init(); +#endif // MELONCAP +      AudioSync = SDL_CreateCond();      AudioSyncLock = SDL_CreateMutex(); @@ -2820,6 +2832,10 @@ int main(int argc, char** argv)      if (MicWavBuffer) delete[] MicWavBuffer; +#ifdef MELONCAP +    MelonCap::DeInit(); +#endif // MELONCAP +      if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]);      if (ScreenBitmap[1]) uiDrawFreeBitmap(ScreenBitmap[1]);  |