/******************************Module*Header*******************************\
* Module Name: pointer.c
*
* Contains the pointer management functions.
*
* Copyright (c) 1992-1995 Microsoft Corporation
*
\**************************************************************************/

#include "precomp.h"


ULONG SetMonoHwPointerShape(
    SURFOBJ    *pso,
    SURFOBJ    *psoMask,
    SURFOBJ    *psoColor,
    XLATEOBJ   *pxlo,
    LONG        xHot,
    LONG        yHot,
    LONG        x,
    LONG        y,
    RECTL      *prcl,
    FLONG       fl);

BYTE jRepMask2[] =
{
    0x00, 0x05, 0x0a, 0x0f, 0x50, 0x55, 0x5a, 0x5f,
    0xa0, 0xa5, 0xaa, 0xaf, 0xf0, 0xf5, 0xfa, 0xff,
};

/*****************************************************************************
 * DrvMovePointer -
 ****************************************************************************/
VOID DrvMovePointer(
    SURFOBJ*    pso,
    LONG        x,
    LONG        y,
    RECTL*      prcl)
{
    PPDEV   ppdev;
    INT xx, yy;

    ppdev = (PPDEV) pso->dhpdev;

    // If x is -1 then take down the cursor.

    if (x == -1)
    {
        DISABLE_SPRITE(ppdev);
        return;
    }

    // Adjust the actual pointer position depending upon
    // the hot spot.

    x -= ppdev->W32SpriteData.ptlHot.x;
    y -= ppdev->W32SpriteData.ptlHot.y;

    if (ppdev->ulChipID == ET6000)
    {
        char    xPreset = 0;
        char    yPreset = 0;

        // We may have disabled the sprite if it went off the screen.
        // So, now have to detect if we did and re-enable it if necessary.

        if (ppdev->W32SpriteData.fl & POINTER_DISABLED)
        {
            ENABLE_SPRITE(ppdev);
        }

        if (x < 0)
        {
            xPreset = (CHAR)~x;
            x = 0;
        }
        if (y < 0)
        {
            yPreset = (CHAR)~y;
            y = 0;
        }

        ET6K_HORZ_PRESET(ppdev, xPreset);
        ET6K_VERT_PRESET(ppdev, yPreset);
        ET6K_SPRITE_HORZ_POSITION(ppdev, x);
        ET6K_SPRITE_VERT_POSITION(ppdev, y);
        return;
    }
    else
    {
        //
        // Adjust pointer x position for color depth
        //

        x *= ppdev->cBpp;

        // Yet another bug.
        // If the cursor is moved entirely off the screen, it could cause
        // the screen to shake.  So, we have to disable the cursor if it
        // is moved entirely off the screen.

        if ((x < - ((LONG) (ppdev->W32SpriteData.szlPointer.cx))) ||
            (x > ((LONG) (ppdev->cxScreen * ppdev->cBpp))) ||
            (y < - ((LONG) (ppdev->W32SpriteData.szlPointer.cy))) ||
            (y > ((LONG) (ppdev->cyScreen))))
        {
            DISABLE_SPRITE(ppdev);
            return;
        }

        // We may have disabled the sprite if it went off the screen.
        // So, now have to detect if we did and re-enable it if necessary.
        // (remembering to keep track of the state).

        if (ppdev->W32SpriteData.fl & POINTER_DISABLED)
        {
            ENABLE_SPRITE(ppdev);
        }

        // The W32 non-rev-B has a problem with a vertical offset of 0x3f.
        // All the other W32's have a problem with the last nibble being
        // 0x0F for both the horizontal and the verical.
        // Never set the bad presets on the chips in question.

        if (x <= 0)
        {
            if ((ppdev->ulChipID == W32) &&
                (ppdev->ulRevLevel != REV_B))
            {
                xx = -x;
                if ((xx & 0x0F) == 0x0F)
                    xx &= ~0x01;

                SET_HORZ_PRESET(xx);
            }
            else
            {
                SET_HORZ_PRESET(-x);
            }
            x = 0;
        }
        else
        {
            SET_HORZ_PRESET(0);
        }

        if (y <= 0)
        {
            if (ppdev->ulChipID == W32)
            {
                yy = -y;

                if (ppdev->ulRevLevel != REV_B)
                {
                    if (yy == 0x3F)
                        yy = 0x3E;
                }
                else
                {
                    if ((yy & 0x0F) == 0x0F)
                        yy &= ~0x01;
                }
                SET_VERT_PRESET(yy);
            }
            else
            {
                SET_VERT_PRESET(-y);
            }

            y = 0;
        }
        else
        {
            SET_VERT_PRESET(0);
        }

        // You guessed it.  Another bug.
        // On the W32 Rev B you can not put the cursor on the bottom line
        // of the display.  And if were in interlaced mode you can't put it
        // on the bottom two lines.

        if ((ppdev->ulChipID == W32) &&
            (ppdev->ulRevLevel == REV_B))
        {
            INT i;

            if (y > (i = ppdev->cyScreen - 2))
            {
                OUTP(0x3D4, 0x35);
                if (INP(0x3D5) & 0x80)
                    y = i;
            }
            else if (y > (i+1))
            {
                y = i+1;
            }
        }

        //////////////////////////////////////////////////////
        // Set the position of the sprite.

        if ((ppdev->ulChipID == W32I) ||
            (ppdev->ulChipID == W32P))
        {
            // You bet, one more bug, and this one is a lulu.
            // First we have to set the vertical position before the horz
            // position.  Why you ask, because, if this is a W32 Rev B or later
            // we may have to toggle a bit in a test register to really set the
            // vertical position, but of course we don't want to set anything
            // else at this point.

            BYTE    status, byte;

            // Wait for horz display interval.

            while ( (INP(0x3DA) & 0x02));
            while (!((status = INP(0x3DA)) & 0x02));

            SET_VERT_POSITION(y);

            // Check if the sprite is being displayed at this very moment.
            // And if it is then skip the test bit stuff.

            if (!(status & 0x04))
            {
                // Looks like we will have to toggle the test bit to
                // really set the vertical position.

                ENABLE_KEY(ppdev);

                OUTP(0x3D4, 0x37);
                byte = INP(0x3D5);
                byte |= 0x40;
                OUTP(0x3D5, byte);
                byte &= ~0x40;
                OUTP(0x3D5, byte);

                DISABLE_KEY(ppdev);
            }

            SET_HORZ_POSITION(x);
        }
        else
        {
            // For consistency sake, we're going to set the vertical first
            // even for non-W32 Rev B chips.

            SET_VERT_POSITION(y);
            SET_HORZ_POSITION(x);
        }

        return;
    }
}


/******************************Public*Routine******************************\
* DrvSetPointerShape
*
* Sets the new pointer shape.
\**************************************************************************/

ULONG DrvSetPointerShape(
    SURFOBJ    *pso,
    SURFOBJ    *psoMask,
    SURFOBJ    *psoColor,
    XLATEOBJ   *pxlo,
    LONG        xHot,
    LONG        yHot,
    LONG        x,
    LONG        y,
    RECTL      *prcl,
    FLONG       fl)
{
    PPDEV   ppdev;
    ULONG   ulRet;

    ppdev = (PPDEV) pso->dhpdev;

    if (ppdev->flCaps & CAPS_SW_POINTER)
    {
        return(SPS_DECLINE);
    }

    // Save the hot spot and dimensions of the cursor in globals.

    ppdev->W32SpriteData.ptlHot.x = xHot;
    ppdev->W32SpriteData.ptlHot.y = yHot;

    ppdev->W32SpriteData.szlPointer.cx = psoMask->sizlBitmap.cx * ppdev->cBpp;
    ppdev->W32SpriteData.szlPointer.cy = psoMask->sizlBitmap.cy / 2;

    if (psoColor != NULL)
    {
        // Disable the mono hardware pointer, and decline the pointer
        // shape

        DISABLE_SPRITE(ppdev);

        ulRet = SPS_DECLINE;

    }
    else
    {
        // Take care of the monochrome pointer.

        ulRet = SetMonoHwPointerShape(pso, psoMask, psoColor, pxlo,
                                      xHot, yHot, x, y, prcl, fl);
        if (ulRet == SPS_DECLINE)
        {
            DISABLE_SPRITE(ppdev);
        }
    }

    return (ulRet);
}

/*****************************************************************************
 * DrvSetMonoHwPointerShape -
 ****************************************************************************/
ULONG SetMonoHwPointerShape(
    SURFOBJ    *pso,
    SURFOBJ    *psoMask,
    SURFOBJ    *psoColor,
    XLATEOBJ   *pxlo,
    LONG        xHot,
    LONG        yHot,
    LONG        x,
    LONG        y,
    RECTL      *prcl,
    FLONG       fl)
{

    INT     i,
            j,
            cxMask,
            cyMask,
            cyAND,
            cxAND,
            cyXOR,
            cxXOR;

    PBYTE   pjAND,
            pjXOR;

    INT     lDelta;

    PPDEV   ppdev;

    INT     ix,
            iy,
            is,
            ip,
            iBit,
            jAndByte,
            jXorByte,
            jSpriteBits,
            jSpriteByte;

    INT     njSpriteBuffer;
    BOOL    bDetectXOR;

    BYTE*   pjBase;

    BYTE    ajAndMask[64][8],
            ajXorMask[64][8];

    BYTE    ajW32Sprite[1024];
    LONG    cBpp;
    INT     ndx = 0;

        ppdev    = (PPDEV) pso->dhpdev;
        pjBase   = ppdev->pjBase;
        cBpp     = ppdev->cBpp;

        // The W32 does not handle an XOR and an AND.
        // So, set a bool if we need to detect this condition.

        bDetectXOR = FALSE;
        if (ppdev->ulChipID == W32)
            bDetectXOR = TRUE;

        // If the mask is NULL this implies the pointer is not
        // visible.

        if (psoMask == NULL)
        {
            DISABLE_SPRITE(ppdev);
            return (SPS_ACCEPT_NOEXCLUDE);
        }

        // Init the AND and XOR masks.

        memset (ajAndMask, 0xFFFFFFFF, 512);
        memset (ajXorMask, 0, 512);

        // Get the bitmap dimensions.

        cxMask = psoMask->sizlBitmap.cx;
        cyMask = psoMask->sizlBitmap.cy;

        cyAND = cyXOR = cyMask / 2;
        cxAND = cxXOR = cxMask / 8;

        // Set up pointers to the AND and XOR masks.

        pjAND  =  psoMask->pvScan0;
        lDelta = psoMask->lDelta;
        pjXOR  = pjAND + (cyAND * lDelta);

        // Copy the AND mask.

        for (i = 0; i < cyAND; i++)
        {
            // Copy over a line of the AND mask.

            for (j = 0; j < cxAND; j++)
            {
                ajAndMask[i][j] = pjAND[j];
            }

            // point to the next line of the AND mask.

            pjAND += lDelta;
        }

        // Copy the XOR mask.

        for (i = 0; i < cyXOR; i++)
        {
            // Copy over a line of the XOR mask.

            for (j = 0; j < cxXOR; j++)
            {
                ajXorMask[i][j] = pjXOR[j];
            }

            // point to the next line of the XOR mask.

            pjXOR += lDelta;
        }

        // Build up the sprite from NT's And and Xor masks.

        // Init the indexes into the sprite buffer (is) and the
        // index for the bit pairs (ip).

        is = 0;
        ip = 0;

        // Outer most loop goes over NT's And and Xor rows.

        for (iy = 0; iy < 64; iy++)
        {
            // loop over the columns.
            for (ix = 0; ix < 8; ix++)
            {
                // pickup a source byte for each mask.
                jAndByte = ajAndMask[iy][ix];
                jXorByte = ajXorMask[iy][ix];

                // loop over the bits in the byte.
                for (iBit = 0x80; iBit != 0; iBit >>= 1)
                {
                    // init the sprite  bitpair.
                    jSpriteBits = 0x0;

                    // Set the sprite bit pairs.
                    if (jAndByte & iBit)
                        jSpriteBits |= 0x02;

                    if (jXorByte & iBit)
                        jSpriteBits |= 0x01;

                    if (bDetectXOR == TRUE)
                    {
                        if ((jAndByte & iBit) && (jXorByte & iBit))
                        {
                            return (SPS_DECLINE);
                        }
                    }

                    if ((ip % 4) == 0)
                    {
                        // If all 4 bit pairs in this byte are filled in
                        // flush the sprite byte to the sprite byte array.
                        // and set the first bit pair.
                        if (ip != 0)
                        {
                            ajW32Sprite[is++] = (BYTE)jSpriteByte;
                        }
                        jSpriteByte = jSpriteBits;
                    }
                    else
                    {
                        // If the sprite byte is not full, shift the bit pair
                        // into position, and or it into the sprite byte.
                        jSpriteBits <<= (ip % 4) * 2;
                        jSpriteByte  |= jSpriteBits;
                    }

                    // bump the bit pair counter.
                    ip++;
                }
            }
        }

        // Flush the last byte.
        ajW32Sprite[is++] = (BYTE)jSpriteByte;

        // Disable the pointer.
        DISABLE_SPRITE(ppdev);

        DISPDBG((1,"setting sprite shape at offset (%xh)", ppdev->cjPointerOffset));

        if (ppdev->ulChipID == ET6000)
        {
            BYTE * pjDst = ppdev->pjScreen + ppdev->cjPointerOffset;
            BYTE * pjSrc = ajW32Sprite;

            for (i = 0; i < 1024; i++)
            {
                *pjDst++ = *pjSrc++;
            }
        }
        else
        {
            ndx = 0;
            CP_MMU_BP0(ppdev, pjBase, ppdev->cjPointerOffset);
            if (cBpp == 1)
            {
                for (i = 0; i < 1024; i++)
                {
                    //*pjSpriteBuffer++ = ajW32Sprite[i];
                    CP_WRITE_MMU_BYTE(ppdev, 0, ndx, ajW32Sprite[i]);
                    ndx++;
                }
            }
            else if (cBpp == 2)
            {
                for (i = 0; i < 64; i++)
                {
                    for (j = 0; j < 8; j++)
                    {
                        //*pjSpriteBuffer++ = jRepMask2[ajW32Sprite[(16*i)+j] & 0xf];
                        //*pjSpriteBuffer++ = jRepMask2[ajW32Sprite[(16*i)+j] >> 4];
                        CP_WRITE_MMU_BYTE(ppdev, 0, ndx, jRepMask2[ajW32Sprite[(16*i)+j] & 0xf]);
                        ndx++;
                        CP_WRITE_MMU_BYTE(ppdev, 0, ndx, jRepMask2[ajW32Sprite[(16*i)+j] >> 4]);
                        ndx++;
                    }
                }
            }
        }

        // Set the position of the cursor.
        DrvMovePointer(pso, x, y, NULL);

        return (SPS_ACCEPT_NOEXCLUDE);
}



/******************************Public*Routine******************************\
* VOID vDisablePointer
*
\**************************************************************************/

VOID vDisablePointer(
    PDEV*   ppdev)
{
    // Nothing to do, really
}

/******************************Public*Routine******************************\
* VOID vAssertModePointer
*
\**************************************************************************/

VOID vAssertModePointer(
PDEV*   ppdev,
BOOL    bEnable)
{
    BYTE*       pjBase;
    ULONG       ulPhysicalAddr;
    INT         i, j,
                nBytesPerBank,
                njSpriteBuffer,
                n8kBanks,
                nRemainingBytes;

    if (ppdev->flCaps & CAPS_SW_POINTER)
    {
        // With a software pointer, we don't have to do anything.
    }
    else
    {
        DISPDBG((1,"vAssertModePointer: cxMemory = %d", ppdev->cxMemory));
        DISPDBG((1,"vAssertModePointer: cyMemory = %d", ppdev->cyMemory));
        DISPDBG((1,"vAssertModePointer: cxScreen = %d", ppdev->cxScreen));
        DISPDBG((1,"vAssertModePointer: cyScreen = %d", ppdev->cyScreen));

        pjBase = ppdev->pjBase;

        // Take care of the init for the Sprite.

        if (ppdev->ulChipID == ET6000)
        {
            BYTE * pjDst = ppdev->pjScreen + ppdev->cjPointerOffset;

            ET6K_SPRITE_HORZ_POSITION(ppdev, ppdev->cxScreen / 2);      //  Center it.
            ET6K_SPRITE_VERT_POSITION(ppdev, ppdev->cyScreen / 2);      //  Center it.
            ET6K_HORZ_PRESET(ppdev, 0);
            ET6K_VERT_PRESET(ppdev, 0);
            ET6K_SPRITE_START_ADDR(ppdev, ppdev->cjPointerOffset);
            ET6K_SPRITE_COLOR(ppdev, 0xFF00);

            for (i = 0; i < 1024; i++)
            {
                *pjDst++ = 0xaa;
            }
        }
        else
        {
            SET_HORZ_POSITION(ppdev->cxScreen * ppdev->cBpp / 2);
            SET_VERT_POSITION(ppdev->cyScreen / 2);
            SET_HORZ_PRESET(0);
            SET_VERT_PRESET(0);

            SET_SPRITE_START_ADDR(ppdev->cjPointerOffset);
            SET_SPRITE_ROW_OFFSET;

            // Set the CRTCB pixel pan register to 0.

            OUTP(CRTCB_SPRITE_INDEX, CRTCB_PIXEL_PANNING);
            OUTP(CRTCB_SPRITE_DATA, 0);

            // Set the pixel depth to 2 bits per pixel.
            // (even though the doc says this is only for the CRTCB mode and not
            // the sprite mode, the doesn't work unless these values are 0.

            OUTP(CRTCB_SPRITE_INDEX, CRTCB_COLOR_DEPTH);
            OUTP(CRTCB_SPRITE_DATA, 0x01);

            // Set the CRTCB/Sprite control to a 64 X 64 Sprite in overlay mode.

            OUTP(CRTCB_SPRITE_INDEX, CRTCB_SPRITE_CONTROL);
            OUTP(CRTCB_SPRITE_DATA, 0x02);

            // Fill the sprite buffer and the next 17 lines with a transparent
            // pattern.  This is to get around one of the sprite bugs.

            njSpriteBuffer = SPRITE_BUFFER_SIZE;

            nBytesPerBank    = 0x2000;
            n8kBanks         = njSpriteBuffer / nBytesPerBank;
            nRemainingBytes  = njSpriteBuffer % nBytesPerBank;

            for (j = 0; j < n8kBanks; j++)
            {
                // First set Aperture 0 to the sprite buffer address.

                CP_MMU_BP0(ppdev, pjBase, (ppdev->cjPointerOffset + (j * nBytesPerBank)));

                // Reset the linear address to the beginning of this 8K segment

                for (i = 0; i < nBytesPerBank; i++)
                {
                    //*pjSpriteBuffer++ = 0xAA;
                    CP_WRITE_MMU_BYTE(ppdev, 0, i, 0xAA);
                }
            }

            // Set Aperture 0 to the sprite buffer address.

            CP_MMU_BP0(ppdev, pjBase, (ppdev->cjPointerOffset + (j * nBytesPerBank)));

            // Reset the linear address to the beginning of this 8K segment

            for (i = 0; i < nRemainingBytes; i++)
            {
                //*pjSpriteBuffer++ = 0xAA;
                CP_WRITE_MMU_BYTE(ppdev, 0, i, 0xAA);
            }

        }
        ENABLE_SPRITE(ppdev);
    }
}

/******************************Public*Routine******************************\
* BOOL bEnablePointer
*
\**************************************************************************/

BOOL bEnablePointer(
PDEV*   ppdev)
{
    if (ppdev->flCaps & CAPS_SW_POINTER)
    {
        // With a software pointer, we don't have to do anything.
    }
    else
    {
        // Enable the W32 hardware pointer.
    }

    // Actually turn on the pointer:

    vAssertModePointer(ppdev, TRUE);

    DISPDBG((5, "Passed bEnablePointer"));

    return(TRUE);
}