private void renderBG(MMU mmu)
        {
            byte WX    = (byte)(mmu.WX - 7); //WX needs -7 Offset
            byte WY    = mmu.WY;
            byte LY    = mmu.LY;
            byte LCDC  = mmu.LCDC;
            byte SCY   = mmu.SCY;
            byte SCX   = mmu.SCX;
            byte BGP   = mmu.BGP;
            bool isWin = isWindow(LCDC, WY, LY);

            byte y        = isWin ? (byte)(LY - WY) : (byte)(LY + SCY);
            byte tileLine = (byte)((y & 7) * 2);

            ushort tileRow = (ushort)(y / 8 * 32);
            ushort tileMap = isWin ? getWindowTileMapAdress(LCDC) : getBGTileMapAdress(LCDC);

            byte hi = 0;
            byte lo = 0;

            for (int p = 0; p < SCREEN_WIDTH; p++)
            {
                byte x = isWin && p >= WX ? (byte)(p - WX) : (byte)(p + SCX);
                if ((p & 0x7) == 0 || ((p + SCX) & 0x7) == 0)
                {
                    ushort tileCol    = (ushort)(x / 8);
                    ushort tileAdress = (ushort)(tileMap + tileRow + tileCol);

                    ushort tileLoc;
                    if (isSignedAdress(LCDC))
                    {
                        tileLoc = (ushort)(getTileDataAdress(LCDC) + mmu.readVRAM(tileAdress) * 16);
                    }
                    else
                    {
                        tileLoc = (ushort)(getTileDataAdress(LCDC) + ((sbyte)mmu.readVRAM(tileAdress) + 128) * 16);
                    }

                    lo = mmu.readVRAM((ushort)(tileLoc + tileLine));
                    hi = mmu.readVRAM((ushort)(tileLoc + tileLine + 1));
                }

                int colorBit = 7 - (x & 7); //inversed
                int colorId  = GetColorIdBits(colorBit, lo, hi);
                int colorIdThroughtPalette = GetColorIdThroughtPalette(BGP, colorId);

                bmp.SetPixel(p, LY, color[colorIdThroughtPalette]);
            }
        }
        private void renderSprites(MMU mmu)
        {
            byte LY   = mmu.LY;
            byte LCDC = mmu.LCDC;

            for (int i = 0x9C; i >= 0; i -= 4)
            {                                       //0x9F OAM Size, 40 Sprites x 4 bytes:
                int  y    = mmu.readOAM(i) - 16;    //Byte0 - Y Position //needs 16 offset
                int  x    = mmu.readOAM(i + 1) - 8; //Byte1 - X Position //needs 8 offset
                byte tile = mmu.readOAM(i + 2);     //Byte2 - Tile/Pattern Number
                byte attr = mmu.readOAM(i + 3);     //Byte3 - Attributes/Flags

                if ((LY >= y) && (LY < (y + spriteSize(LCDC))))
                {
                    byte palette = isBit(4, attr) ? mmu.OBP1 : mmu.OBP0; //Bit4   Palette number  **Non CGB Mode Only** (0=OBP0, 1=OBP1)

                    int tileRow = isYFlipped(attr) ? spriteSize(LCDC) - 1 - (LY - y) : (LY - y);

                    ushort tileddress = (ushort)(0x8000 + (tile * 16) + (tileRow * 2));
                    byte   lo         = mmu.readVRAM(tileddress);
                    byte   hi         = mmu.readVRAM((ushort)(tileddress + 1));

                    for (int p = 0; p < 8; p++)
                    {
                        int IdPos   = isXFlipped(attr) ? p : 7 - p;
                        int colorId = GetColorIdBits(IdPos, lo, hi);
                        int colorIdThroughtPalette = GetColorIdThroughtPalette(palette, colorId);

                        if ((x + p) >= 0 && (x + p) < SCREEN_WIDTH)
                        {
                            if (!isTransparent(colorId) && (isAboveBG(attr) || isBGWhite(mmu.BGP, x + p, LY)))
                            {
                                bmp.SetPixel(x + p, LY, color[colorIdThroughtPalette]);
                            }
                        }
                    }
                }
            }
        }