/******************************Module*Header**********************************\
*
*                           *******************
*                           * GDI SAMPLE CODE *
*                           *******************
*
* Module Name: pxrxstrp.c
*
* Content:
*
//@@BEGIN_DDKSPLIT
* All the line code in this driver amounts to a big bag of dirt.  Someday,
* I'm going to rewrite it all.  Not today, though (sigh)...
* Original comment!. Rewritten for GLINT. Styled lines could do with more
* work, but Solid lines should be about as optimal as possible without
* rewriting the algorithm that calls these functions.
//@@END_DDKSPLIT
*
* Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-2001 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#include "precomp.h"
#include "pxrx.h"

/**************************************************************************\
*
* BOOL pxrxInitStrips
*
\**************************************************************************/
BOOL pxrxInitStrips( 
    PPDEV ppdev, 
    ULONG ulColor, 
    DWORD logicOp, 
    RECTL *prclClip ) 
{
    DWORD config2D;
    BOOL  bInvalidateScissor = FALSE;
    GLINT_DECL;

    VALIDATE_DD_CONTEXT;

    SET_WRITE_BUFFERS;

    WAIT_PXRX_DMA_TAGS( 7 );

    if( logicOp == __GLINT_LOGICOP_COPY ) 
    {
        config2D = __CONFIG2D_CONSTANTSRC | 
                   __CONFIG2D_FBWRITE;
    } 
    else 
    {
        config2D = __CONFIG2D_LOGOP_FORE(logicOp) | 
                   __CONFIG2D_CONSTANTSRC         | 
                   __CONFIG2D_FBWRITE;

        if( LogicopReadDest[logicOp] ) 
        {
            config2D |= __CONFIG2D_FBDESTREAD;
            SET_READ_BUFFERS;
        }
    }

    LOAD_FOREGROUNDCOLOUR( ulColor );

    if( prclClip ) 
    {
        config2D |= __CONFIG2D_USERSCISSOR;
        QUEUE_PXRX_DMA_TAG( __GlintTagScissorMinXY, 
                            MAKEDWORD_XY(prclClip->left,  prclClip->top   ) );
        QUEUE_PXRX_DMA_TAG( __GlintTagScissorMaxXY, 
                            MAKEDWORD_XY(prclClip->right, prclClip->bottom) );
        
        if(ppdev->cPelSize == GLINTDEPTH32)
        {
            bInvalidateScissor = TRUE;
        }
    }
    
    LOAD_CONFIG2D( config2D );

    SEND_PXRX_DMA_BATCH;

    glintInfo->savedConfig2D = config2D;
    glintInfo->savedLOP = logicOp;
    glintInfo->savedCol = ulColor;
    glintInfo->savedClip = prclClip;

    DISPDBG((DBGLVL, "pxrxInitStrips done"));
    return (bInvalidateScissor);
}

/**************************************************************************\
*
* VOID pxrxResetStrips
*
\**************************************************************************/
VOID pxrxResetStrips( 
    PPDEV ppdev ) 
{
    GLINT_DECL;

    DISPDBG((DBGLVL, "pxrxResetStrips called"));

    // Reset the scissor maximums:
    WAIT_PXRX_DMA_TAGS( 1 );
    QUEUE_PXRX_DMA_TAG( __GlintTagScissorMaxXY, 0x7FFF7FFF );
    SEND_PXRX_DMA_BATCH;
}

/**************************************************************************\
*
* VOID pxrxIntegerLine
*
\**************************************************************************/
BOOL pxrxIntegerLine( 
    PPDEV ppdev, 
    LONG  X1, 
    LONG  Y1, 
    LONG  X2, 
    LONG  Y2 ) 
{
    LONG    dx, dy, adx, ady;
    GLINT_DECL;

    // Convert points to INT format:
    X1 >>= 4;
    Y1 >>= 4;
    X2 >>= 4;
    Y2 >>= 4;

    if( (adx = dx = X2 - X1) < 0 )
    {
        adx = -adx;
    }

    if( (ady = dy = Y2 - Y1) < 0 )
    {
        ady = -ady;
    }

    WAIT_PXRX_DMA_TAGS( 3+2 );
    if( adx > ady ) 
    {
        // X major line:
        if( ady == dy ) 
        {
            // +ve minor delta
            if((ady)        && 
               (adx != ady) && 
               (adx > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_P) ) 
            {
                DISPDBG((DBGLVL, "pxrxIntegerLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxIntegerLine: [X +] delta = (%d, %d), "
                             "bias = (0x%08x)", 
                             dx, dy, P3_LINES_BIAS_P));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagYBias, P3_LINES_BIAS_P );
        } 
        else 
        {
            // -ve minor delta
            if( (ady)        && 
                (adx != ady) && 
                (adx > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_N) ) 
            {
                DISPDBG((DBGLVL, "pxrxIntegerLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxIntegerLine: [X -] delta = (%d, %d), "
                             "bias = (0x%08x)", 
                             dx, dy, P3_LINES_BIAS_N));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagYBias, P3_LINES_BIAS_N );
        }
    } 
    else 
    {
        // Y major line:
        if( adx == dx ) 
        {
            // +ve minor delta
            if( (adx)        && 
                (adx != ady) && 
                (ady > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_P) ) 
            {
                DISPDBG((DBGLVL, "pxrxIntegerLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxIntegerLine: [Y +] delta = (%d, %d), "
                             "bias = (0x%08x)", 
                             dx, dy, P3_LINES_BIAS_P));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagXBias, P3_LINES_BIAS_P );
        } 
        else 
        {
            // -ve minor delta
            if( (adx)        && 
                (adx != ady) && 
                (ady > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_N) ) 
            {
                DISPDBG((DBGLVL, "pxrxIntegerLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxIntegerLine: [Y -] delta = (%d, %d), "
                             "bias = (0x%08x)", 
                             dx, dy, P3_LINES_BIAS_N));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagXBias, P3_LINES_BIAS_N );
        }
    }

    QUEUE_PXRX_DMA_INDEX3( __DeltaTagLineCoord0, 
                           __DeltaTagLineCoord1, 
                           __DeltaTagDrawLine2D01 );
                           
    QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X1, Y1) );
    QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X2, Y2) );
    QUEUE_PXRX_DMA_DWORD( 0 );

    SEND_PXRX_DMA_BATCH;

    glintInfo->lastLine = 1;

    DISPDBG((DBGLVL, "pxrxIntegerLine done"));
    return TRUE;
}

/**************************************************************************\
*
* BOOL pxrxContinueLine
*
\**************************************************************************/
BOOL pxrxContinueLine( 
    PPDEV ppdev, 
    LONG  X1, 
    LONG  Y1, 
    LONG  X2, 
    LONG  Y2 ) 
{
    LONG    dx, dy, adx, ady;
    GLINT_DECL;

    // Convert points to INT format:
    X1 >>= 4;
    Y1 >>= 4;
    X2 >>= 4;
    Y2 >>= 4;

    if( (adx = dx = X2 - X1) < 0 )
    {
        adx = -adx;
    }

    if( (ady = dy = Y2 - Y1) < 0 )
    {
        ady = -ady;
    }

    WAIT_PXRX_DMA_TAGS( 3+2 );
    
    if( adx > ady ) 
    {
        // X major line:
        if( ady == dy ) 
        {
            // +ve minor delta
            if( (ady)        && 
                (adx != ady) && 
                (adx > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_P) ) 
            {
                DISPDBG((DBGLVL, "pxrxContinueLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxContinueLine: delta = (%d, %d), "
                             "bias = (0x%08x), last = %d", 
                             dx, dy, P3_LINES_BIAS_P, glintInfo->lastLine));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagYBias, P3_LINES_BIAS_P );
        } 
        else 
        {
            // -ve minor delta
            if( (ady)        && 
                (adx != ady) && 
                (adx > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_N) ) 
            {
                DISPDBG((DBGLVL, "pxrxContinueLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxContinueLine: delta = (%d, %d), "
                             "bias = (0x%08x), last = %d", 
                             dx, dy, P3_LINES_BIAS_N, glintInfo->lastLine));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagYBias, P3_LINES_BIAS_N );
        }
    } 
    else 
    {
        // Y major line:
        if( adx == dx ) 
        {
            // +ve minor delta
            if( (adx)        && 
                (adx != ady) && 
                (ady > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_P) ) 
            {
                DISPDBG((DBGLVL, "pxrxContinueLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxContinueLine: delta = (%d, %d), "
                             "bias = (0x%08x), last = %d", 
                             dx, dy, P3_LINES_BIAS_P, glintInfo->lastLine));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagXBias, P3_LINES_BIAS_P );
        } 
        else 
        {
            // -ve minor delta
            if( (adx)        && 
                (adx != ady) && 
                (ady > MAX_LENGTH_CONFORMANT_P3_INTEGER_LINES_N) ) 
            {
                DISPDBG((DBGLVL, "pxrxContinueLine failed"));
                return FALSE;
            }

            DISPDBG((DBGLVL, "pxrxContinueLine: delta = (%d, %d), "
                             "bias = (0x%08x), last = %d", 
                             dx, dy, P3_LINES_BIAS_N, glintInfo->lastLine));
                             
            QUEUE_PXRX_DMA_TAG( __DeltaTagXBias, P3_LINES_BIAS_N );
        }
    }

    if( glintInfo->lastLine == 0 ) 
    {
        QUEUE_PXRX_DMA_INDEX2( __DeltaTagLineCoord1, 
                               __DeltaTagDrawLine2D01 );
        QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X2, Y2) );
        QUEUE_PXRX_DMA_DWORD( 0 );
        glintInfo->lastLine = 1;
    } 
    else if( glintInfo->lastLine == 1 ) 
    {
        QUEUE_PXRX_DMA_INDEX2( __DeltaTagLineCoord0, 
                               __DeltaTagDrawLine2D10 );
        QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X2, Y2) );
        QUEUE_PXRX_DMA_DWORD( 0 );
        glintInfo->lastLine = 0;
    }
    else
    {
        // lastline == 2
        QUEUE_PXRX_DMA_INDEX3( __DeltaTagLineCoord0, 
                               __DeltaTagLineCoord1, 
                               __DeltaTagDrawLine2D01 );
        QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X1, Y1) );
        QUEUE_PXRX_DMA_DWORD( MAKEDWORD_XY(X2, Y2) );
        QUEUE_PXRX_DMA_DWORD( 0 );

        glintInfo->lastLine = 1;
    }

    SEND_PXRX_DMA_BATCH;

    DISPDBG((DBGLVL, "pxrxContinueLine done"));
    return TRUE;
}


/******************************Public*Routine******************************\
* VOID vPXRXSolidHorizontalLine
*
* Draws left-to-right x-major near-horizontal lines using short-stroke
* vectors.  
*
\**************************************************************************/

VOID vPXRXSolidHorizontalLine( 
    PDEV      *ppdev, 
    STRIP     *pStrip, 
    LINESTATE *pLineState )
{
    LONG    cStrips;
    PLONG   pStrips;
    LONG    iCurrent;
    GLINT_DECL;

    cStrips = pStrip->cStrips;

    WAIT_PXRX_DMA_TAGS( 10 );

    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom, 0 );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXSub, 0 );

    // Set up the start point
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(pStrip->ptlStart.x) );
    QUEUE_PXRX_DMA_TAG( __GlintTagStartY,    INTtoFIXED(pStrip->ptlStart.y) );

    // Set up the deltas for rectangle drawing. Also set Y return value.
    if( !(pStrip->flFlips & FL_FLIP_V) ) 
    {
        QUEUE_PXRX_DMA_TAG( __GlintTagdY,       INTtoFIXED(1) );
        pStrip->ptlStart.y += cStrips;
    } 
    else 
    {
        QUEUE_PXRX_DMA_TAG( __GlintTagdY,       INTtoFIXED(-1) );
        pStrip->ptlStart.y -= cStrips;
    }

    pStrips = pStrip->alStrips;

    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    iCurrent = pStrip->ptlStart.x + *pStrips++; // Xsub, Start of next strip
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(iCurrent) );
    QUEUE_PXRX_DMA_TAG( __GlintTagCount,     1 );// Rectangle 1 scanline high
    QUEUE_PXRX_DMA_TAG( __GlintTagRender,    __RENDER_TRAPEZOID_PRIMITIVE );

    if( --cStrips ) 
    {
        while( cStrips > 1 ) 
        {
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            iCurrent += *pStrips++;
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(iCurrent) );
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, 1 );

            WAIT_PXRX_DMA_TAGS( 2 + 2 );

            // Second strip of each pair to fill. XDom is valid. Need new XSub
            iCurrent += *pStrips++;
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(iCurrent) );
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewSub, 1 );

            cStrips -=2;
        }

        // We may have one last line to draw. Xsub will be valid.
        if( cStrips ) 
        {
            iCurrent += *pStrips++;
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(iCurrent) );
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, 1 );
        }
    }

    SEND_PXRX_DMA_BATCH;

    // Return last point. Y already calculated when we knew the direction.
    pStrip->ptlStart.x = iCurrent;
}

/******************************Public*Routine******************************\
* VOID vPXRXSolidVertical
*
* Draws left-to-right y-major near-vertical lines using short-stroke
* vectors.  
*
\**************************************************************************/

VOID vPXRXSolidVerticalLine( 
    PDEV      *ppdev, 
    STRIP     *pStrip, 
    LINESTATE *pLineState )
{
    LONG    cStrips, yDir;
    PLONG   pStrips;
    LONG    iCurrent, iLen, iLenSum;
    GLINT_DECL;

    cStrips = pStrip->cStrips;

    WAIT_PXRX_DMA_TAGS( 10 );

    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,        0 );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXSub,        0 );

    // Set up the start point
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(pStrip->ptlStart.x) );
    QUEUE_PXRX_DMA_TAG( __GlintTagStartY,    INTtoFIXED(pStrip->ptlStart.y) );

    // Set up the deltas for rectangle drawing.
    if( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }
    
    QUEUE_PXRX_DMA_TAG( __GlintTagdY, INTtoFIXED(yDir) );

    pStrips = pStrip->alStrips;

    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    iCurrent = pStrip->ptlStart.x + 1;          // Xsub, Start of next strip
    iLenSum = (iLen = *pStrips++);
    
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(iCurrent) );
    QUEUE_PXRX_DMA_TAG( __GlintTagCount, iLen ); // Rectangle 1 scanline high
    QUEUE_PXRX_DMA_TAG( __GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE );

    if( --cStrips ) 
    {
        while( cStrips > 1 ) 
        {
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            iCurrent++;
            iLenSum += (iLen = *pStrips++);
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(iCurrent) );
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, iLen );

            WAIT_PXRX_DMA_TAGS( 2 + 2 );

            // Second strip of each pair to fill. XDom is valid. Need new XSub
            iCurrent++;
            iLenSum += (iLen = *pStrips++);
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(iCurrent) );
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewSub, iLen );      
            cStrips -=2;
        }

        // We may have one last line to draw. Xsub will be valid.
        if( cStrips ) 
        {
            iCurrent++;
            iLenSum += (iLen = *pStrips++);
            QUEUE_PXRX_DMA_TAG(__GlintTagStartXDom, INTtoFIXED(iCurrent));
            QUEUE_PXRX_DMA_TAG(__GlintTagContinueNewDom, iLen);
        }
    }

    SEND_PXRX_DMA_BATCH;

    // Return last point. 
    pStrip->ptlStart.x = iCurrent;
    pStrip->ptlStart.y += iLenSum * yDir;
}

/******************************Public*Routine******************************\
* VOID vPXRXSolidDiagonalVertical
*
* Draws left-to-right y-major near-diagonal lines using short-stroke
* vectors.  
*
\**************************************************************************/

VOID vPXRXSolidDiagonalVerticalLine( 
    PDEV      *ppdev, 
    STRIP     *pStrip, 
    LINESTATE *pLineState )
{
    LONG    cStrips, yDir;
    PLONG   pStrips;
    LONG    iCurrent, iLen, iLenSum;
    GLINT_DECL;

    cStrips = pStrip->cStrips;

    if( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }

    WAIT_PXRX_DMA_TAGS( 10 );

    // Set up the deltas for rectangle drawing.
    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,        INTtoFIXED(1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXSub,        INTtoFIXED(1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagdY,           INTtoFIXED(yDir) );

    pStrips = pStrip->alStrips;

    // We have to do first strip manually, as we have to use RENDER
    // for the first strip, and CONTINUENEW... for the following strips
    QUEUE_PXRX_DMA_TAG( __GlintTagStartY,    INTtoFIXED(pStrip->ptlStart.y) );
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(pStrip->ptlStart.x+1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(pStrip->ptlStart.x) );

    iLenSum = (iLen = *pStrips++);
    iCurrent = pStrip->ptlStart.x + iLen - 1;       // Start of next strip

    QUEUE_PXRX_DMA_TAG( __GlintTagCount, iLen);     // Trap iLen scanline high
    QUEUE_PXRX_DMA_TAG( __GlintTagRender, __RENDER_TRAPEZOID_PRIMITIVE);

    if( --cStrips ) 
    {
        while( cStrips > 1 ) 
        {
            // First strip of each pair to fill. XSub is valid. Need new Xdom
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(iCurrent) );
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, iLen );

            WAIT_PXRX_DMA_TAGS( 2 + 2 );

            // Second strip of each pair to fill. XDom is valid. Need new XSub
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXSub, INTtoFIXED(iCurrent) );
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewSub, iLen );      

            cStrips -=2;
        }

        // We may have one last line to draw. Xsub will be valid.
        if (cStrips) 
        {
            QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom, INTtoFIXED(iCurrent) );
            iLenSum += (iLen = *pStrips++);
            iCurrent += iLen - 1;
            QUEUE_PXRX_DMA_TAG( __GlintTagContinueNewDom, iLen );
        }
    }

    SEND_PXRX_DMA_BATCH;

    // Return last point. 
    pStrip->ptlStart.x = iCurrent;
    pStrip->ptlStart.y += iLenSum * yDir;
}

/******************************Public*Routine******************************\
* VOID vPXRXSolidDiagonalHorizontalLine
*
* Draws left-to-right x-major near-diagonal lines using short-stroke
* vectors.  
*
\**************************************************************************/

VOID vPXRXSolidDiagonalHorizontalLine( 
    PDEV      *ppdev, 
    STRIP     *pStrip, 
    LINESTATE *pLineState )
{
    LONG    cStrips, yDir, xCurrent, yCurrent, iLen;
    PLONG   pStrips;
    GLINT_DECL;

    // This routine has to be implemented in a different way to the other 3
    // solid line drawing functions because the rasterizer unit will not 
    // produce 2 pixels on the same scanline without a lot of effort in 
    // producing delta values. In this case, we have to draw a complete new
    // primitive for each strip. Therefore, we have to use lines rather than
    // trapezoids to generate the required strips. With lines we use 4 messages
    // per strip, where trapezoids would use 5.

    cStrips = pStrip->cStrips;

    if( !(pStrip->flFlips & FL_FLIP_V) )
    {
        yDir = 1;
    }
    else
    {
        yDir = -1;
    }

    pStrips = pStrip->alStrips;

    xCurrent = pStrip->ptlStart.x;
    yCurrent = pStrip->ptlStart.y;

    WAIT_PXRX_DMA_TAGS( 3 + 4 );

    // Set up the deltas for rectangle drawing.
    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,    INTtoFIXED(1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXSub,    INTtoFIXED(1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagdY,       INTtoFIXED(yDir) );

    while( TRUE ) 
    {
        // Set up the start point
        QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom,    INTtoFIXED(xCurrent) );
        QUEUE_PXRX_DMA_TAG( __GlintTagStartY,       INTtoFIXED(yCurrent) );

        iLen = *pStrips++;
        QUEUE_PXRX_DMA_TAG( __GlintTagCount, iLen );
        QUEUE_PXRX_DMA_TAG( __GlintTagRender, __RENDER_LINE_PRIMITIVE );

        xCurrent += iLen;
        if( yDir > 0 ) 
        {
            yCurrent += iLen - 1;
        }
        else
        {
            yCurrent -= iLen - 1;
        }

        if( !(--cStrips) )
        {
            break;
        }

        WAIT_PXRX_DMA_TAGS( 4 );
    }

    SEND_PXRX_DMA_BATCH;

    // Return last point. 
    pStrip->ptlStart.x = xCurrent;
    pStrip->ptlStart.y = yCurrent;
}


/******************************Public*Routine******************************\
* VOID vPXRXStyledHorizontal
*
* Takes the list of strips that define the pixels that would be lit for
* a solid line, and breaks them into styling chunks according to the
* styling information that is passed in.
*
* This particular routine handles x-major lines that run left-to-right,
* and are comprised of horizontal strips.  It draws the dashes using
* short-stroke vectors.
*
* The performance of this routine could be improved significantly if
* anyone cared enough about styled lines improve it.
*
\**************************************************************************/

VOID vPXRXStyledHorizontalLine( 
    PDEV      *ppdev, 
    STRIP     *pstrip, 
    LINESTATE *pls )
{
    LONG    x;
    LONG    y;
    LONG    dy;
    LONG*   plStrip;
    LONG    cStrips;
    LONG    cStyle;
    LONG    cStrip;
    LONG    cThis;
    ULONG   bIsGap;
    GLINT_DECL;

//@@BEGIN_DDKSPLIT
    // This routine does some quite complex things with patterns. For ease of
    // implementation on GLINT, I have just changed the relevant parts. This,
    // therefore, is definitly not an optimal solution. However, styled lines
    // are really not that important. If anyone feels inclined to do more work
    // here, then fine! XXXX
//@@END_DDKSPLIT    

    if( pstrip->flFlips & FL_FLIP_V )
    {
        dy  = -1;
    }
    else
    {
        dy  = 1;
    }

    cStrips = pstrip->cStrips;        // Total number of strips we'll do
    plStrip = pstrip->alStrips;        // Points to current strip
    x       = pstrip->ptlStart.x;    // x position of start of first strip
    y       = pstrip->ptlStart.y;    // y position of start of first strip

    // Set up the deltas for horizontal line drawing.
    WAIT_PXRX_DMA_TAGS( 2 );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,    INTtoFIXED(1) );
    QUEUE_PXRX_DMA_TAG( __GlintTagdY,       0 );

    cStrip = *plStrip;              // Number of pels in first strip

    cStyle = pls->spRemaining;      // Number of pels in first 'gap' or 'dash'
    bIsGap = pls->ulStyleMask;      // Tells whether in a 'gap' or a 'dash'

    // ulStyleMask is non-zero if we're in the middle of a 'gap',
    // and zero if we're in the middle of a 'dash':

    if( bIsGap )
    {
        goto SkipAGap;
    }
    else
    {
        goto OutputADash;
    }

PrepareToSkipAGap:

    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:

    bIsGap = ~bIsGap;
    pls->psp++;
    if (pls->psp > pls->pspEnd)
    {
        pls->psp = pls->pspStart;
    }

    cStyle = *pls->psp;

    // If 'cStrip' is zero, we also need a new strip:

    if (cStrip != 0)
    {
        goto SkipAGap;
    }

    // Here, we're in the middle of a 'gap' where we don't have to
    // display anything.  We simply cycle through all the strips
    // we can, keeping track of the current position, until we run
    // out of 'gap':

    while (TRUE)
    {
        // Each time we loop, we move to a new scan and need a new strip:

        y += dy;

        plStrip++;
        cStrips--;
        if (cStrips == 0)
        {
            goto AllDone;
        }

        cStrip = *plStrip;

    SkipAGap:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        x += cThis;

        if (cStyle == 0)
        {
            goto PrepareToOutputADash;
        }
    }

PrepareToOutputADash:

    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:

    bIsGap = ~bIsGap;
    pls->psp++;
    if (pls->psp > pls->pspEnd)
    {
        pls->psp = pls->pspStart;
    }

    cStyle = *pls->psp;

    // If 'cStrip' is zero, we also need a new strip.

    if (cStrip != 0)
    {
        // There's more to be done in the current strip, so set 'y'
        // to be the current scan:

        goto OutputADash;
    }

    while( TRUE ) 
    {
        // Each time we loop, we move to a new scan and need a new strip:

        y += dy;

        plStrip++;
        cStrips--;
        if( cStrips == 0 )
        {
            goto AllDone;
        }

        cStrip = *plStrip;

    OutputADash:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        // With glint we just download the lines to draw

        WAIT_PXRX_DMA_TAGS( 4 );

        QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom,    INTtoFIXED(x) );
        QUEUE_PXRX_DMA_TAG( __GlintTagStartY,       INTtoFIXED(y) );
        QUEUE_PXRX_DMA_TAG( __GlintTagCount,        cThis );
        QUEUE_PXRX_DMA_TAG( __GlintTagRender,       __GLINT_LINE_PRIMITIVE );

        x += cThis;

        if( cStyle == 0 )
        {
            goto PrepareToSkipAGap;
        }
    }

AllDone:

    SEND_PXRX_DMA_BATCH;

    // Update our state variables so that the next line can continue
    // where we left off:

    pls->spRemaining   = cStyle;
    pls->ulStyleMask   = bIsGap;
    pstrip->ptlStart.x = x;
    pstrip->ptlStart.y = y;
}

/******************************Public*Routine******************************\
* VOID vPXRXStyledVertical
*
* Takes the list of strips that define the pixels that would be lit for
* a solid line, and breaks them into styling chunks according to the
* styling information that is passed in.
*
* This particular routine handles y-major lines that run left-to-right,
* and are comprised of vertical strips.  It draws the dashes using
* short-stroke vectors.
*
* The performance of this routine could be improved significantly if
* anyone cared enough about styled lines improve it.
*
\**************************************************************************/

VOID vPXRXStyledVerticalLine( 
    PDEV      *ppdev, 
    STRIP     *pstrip, 
    LINESTATE *pls )
{
    LONG    x;
    LONG    y;
    LONG    dy;
    LONG*   plStrip;
    LONG    cStrips;
    LONG    cStyle;
    LONG    cStrip;
    LONG    cThis;
    ULONG   bIsGap;
    GLINT_DECL;

//@@BEGIN_DDKSPLIT
    // This routine does some quite complex things with patterns. For ease of
    // implemantation on GLINT, I have just changed the relevant parts. This,
    // therefore, is definitly not an optimal solution. However, styled lines
    // are really not that important. If anyone feels inclined to do more work
    // here, then fine! XXXX
//@@END_DDKSPLIT

    if( pstrip->flFlips & FL_FLIP_V )
    {
        dy = -1;
    }
    else
    {
        dy = 1;
    }

    cStrips = pstrip->cStrips;        // Total number of strips we'll do
    plStrip = pstrip->alStrips;        // Points to current strip
    x       = pstrip->ptlStart.x;    // x position of start of first strip
    y       = pstrip->ptlStart.y;    // y position of start of first strip

    // Set up the deltas for vertical line drawing.
    WAIT_PXRX_DMA_TAGS( 2 );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,    0 );
    QUEUE_PXRX_DMA_TAG( __GlintTagdY,       INTtoFIXED(dy) );

    // dxDom and dXSub are initialised to 0, 0, so 
    // we don't need  to re-load them here.

    cStrip = *plStrip;                // Number of pels in first strip

    cStyle = pls->spRemaining;      // Number of pels in first 'gap' or 'dash'
    bIsGap = pls->ulStyleMask;      // Tells whether in a 'gap' or a 'dash'

    // ulStyleMask is non-zero if we're in the middle of a 'gap',
    // and zero if we're in the middle of a 'dash':

    if (bIsGap)
    {
        goto SkipAGap;
    }
    else
    {
        goto OutputADash;
    }

PrepareToSkipAGap:

    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:

    bIsGap = ~bIsGap;
    pls->psp++;
    if (pls->psp > pls->pspEnd)
    {
        pls->psp = pls->pspStart;
    }

    cStyle = *pls->psp;

    // If 'cStrip' is zero, we also need a new strip:

    if (cStrip != 0)
    {
        goto SkipAGap;
    }

    // Here, we're in the middle of a 'gap' where we don't have to
    // display anything.  We simply cycle through all the strips
    // we can, keeping track of the current position, until we run
    // out of 'gap':

    while (TRUE)
    {
        // Each time we loop, we move to a new column and need a new strip:

        x++;

        plStrip++;
        cStrips--;
        if (cStrips == 0)
        {
            goto AllDone;
        }

        cStrip = *plStrip;

    SkipAGap:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        y += (dy > 0) ? cThis : -cThis;

        if (cStyle == 0)
        {
            goto PrepareToOutputADash;
        }
    }

PrepareToOutputADash:

    // Advance in the style-state array, so that we can find the next
    // 'dot' that we'll have to display:

    bIsGap = ~bIsGap;
    pls->psp++;
    if (pls->psp > pls->pspEnd)
    {
        pls->psp = pls->pspStart;
    }

    cStyle = *pls->psp;

    // If 'cStrip' is zero, we also need a new strip.

    if (cStrip != 0)
    {
        goto OutputADash;
    }

    while (TRUE)
    {
        // Each time we loop, we move to a new column and need a new strip:

        x++;

        plStrip++;
        cStrips--;
        if (cStrips == 0)
        {
            goto AllDone;
        }

        cStrip = *plStrip;

    OutputADash:

        cThis   = min(cStrip, cStyle);
        cStyle -= cThis;
        cStrip -= cThis;

        // With glint we just download the lines to draw

        WAIT_PXRX_DMA_TAGS( 4 );

        QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom,    INTtoFIXED(x) );
        QUEUE_PXRX_DMA_TAG( __GlintTagStartY,       INTtoFIXED(y) );
        QUEUE_PXRX_DMA_TAG( __GlintTagCount,        cThis );
        QUEUE_PXRX_DMA_TAG( __GlintTagRender,       __GLINT_LINE_PRIMITIVE );

        y += (dy > 0) ? cThis : -cThis;

        if( cStyle == 0 )
        {
            goto PrepareToSkipAGap;
        }
    }

AllDone:

    SEND_PXRX_DMA_BATCH;

    // Update our state variables so that the next line can continue
    // where we left off:
    pls->spRemaining   = cStyle;
    pls->ulStyleMask   = bIsGap;
    pstrip->ptlStart.x = x;
    pstrip->ptlStart.y = y;
}

/**************************************************************************\
 * For a given sub-pixel coordinate (x.m, y.n) in 28.4 fixed point
 * format this array is indexed by (m,n) and indicates whether the
 * given sub-pixel is within a GIQ diamond. m coordinates run left
 * to right; n coordinates ru top to bottom so index the array with
 * ((n<<4)+m). The array as seen here really contains 4 quarter
 * diamonds.
\**************************************************************************/
static unsigned char    in_diamond[] = {
/*          0 1 2 3 4 5 6 7 8 9 a b c d e f          */

/* 0 */     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 0 */
/* 1 */     1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,    /* 1 */
/* 2 */     1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,    /* 2 */
/* 3 */     1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,    /* 3 */
/* 4 */     1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,    /* 4 */
/* 5 */     1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,    /* 5 */
/* 6 */     1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,    /* 6 */
/* 7 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 7 */
/* 8 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 8 */
/* 9 */     1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 9 */
/* a */     1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,    /* a */
/* b */     1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,    /* b */
/* c */     1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,    /* c */
/* d */     1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,    /* d */
/* e */     1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,    /* e */
/* f */     1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,    /* f */

/*          0 1 2 3 4 5 6 7 8 9 a b c d e f          */
};
/*
 * For lines with abs(slope) != 1 use IN_DIAMOND to determine if an
 * end point is in a diamond. For lines of slope = 1 use IN_S1DIAMOND.
 * For lines of slope = -1 use IN_SM1DIAMOND. The last two are a bit
 * strange. The documentation leaves us with a problem for slope 1
 * lines which run exactly betwen the diamonds. According to the docs
 * such a line can enter a diamond, leave it and enter again. This is
 * plainly rubbish so along the appropriate edge of the diamond we
 * consider a slope 1 line to be inside the diamond. This is the
 * bottom right edge for lines of slope -1 and the bottom left edge for
 * lines of slope 1.
 */
#define IN_DIAMOND(m, n)    (in_diamond[((m) << 4) + (n)])
#define IN_S1DIAMOND(m, n)  ((in_diamond[((m) << 4) + (n)]) || \
                        ((m) - (n) == 8))
#define IN_SM1DIAMOND(m, n) ((in_diamond[((m) << 4) + (n)]) || \
                        ((m) + (n) == 8))

/**************************************************************************\
*
* BOOL pxrxDrawLine
*
\**************************************************************************/

BOOL pxrxDrawLine( 
    PPDEV ppdev, 
    LONG  fx1, 
    LONG  fy1, 
    LONG  fx2, 
    LONG  fy2 ) 
{
    register LONG   adx, ady, tmp;
    FIX     m1, n1, m2, n2;
    LONG    dx, dy;
    LONG    dX, dY;
    LONG    count, startX, startY;
    GLINT_DECL;

    // This function is only called if we have a line with non integer end 
    // points and the unsigned coordinates are no greater than 15.4. 
    //
    // We can only guarantee to do lines whose coords need <= 12 bits
    // of integer. This is because to get the delta we must shift
    // by 16 bits. This includes 4 bits of fraction which means if
    // we have more than 12 bits of integer we get overrun on the
    // shift. We could use floating point to give us a better 16
    // bits of integer but this requires an extra set of multiplies
    // and divides in order to convert from 28.4 to fp. In any case
    // we have to have a test to reject coords needing > 16 bits
    // of integer.
    // Actually, we can deal with 16.4 coordinates provided dx and dy
    // never require more than 12 bits of integer.
    // So optimise for the common case where the line is completely
    // on the screen (actually 0 to 2047.f). Since the coords have
    // 4 bits of fraction we note that a 32 bit signed number
    // outside the range 0 to 2047.f will have one of its top 17
    // bits set. So logical or all the coords and test against
    // 0xffff8000. This is about as quick a test as we can get for
    // both ends of the line being on the screen. If this test fails
    // then we can check everything else at a leisurely pace.

    // get signed and absolute deltas 
    if ((adx = dx = fx2 - fx1) < 0)
    {
        adx = -adx;
    }
    
    if ((ady = dy = fy2 - fy1) < 0)
    {
        ady = -ady;
    }

    // Refuse to draw any lines whose delta is out of range.
    // We have to shift the delta by 16, so we dont want to loose any
    // precision. 
    if ((adx | ady) & 0xffff8000)
    {
        return(FALSE);
    }

    // fractional bits are used to check if point is in a diamond
    m1 = fx1 & 0xf;
    n1 = fy1 & 0xf;
    m2 = fx2 & 0xf;
    n2 = fy2 & 0xf;


    // The rest of the code is a series of cases. Each one is "called" by a
    // goto. This is simply to keep the nesting down. Main cases are: lines
    // with absolute slope == 1; x-major lines; and y-major lines. We draw
    // lines as they are given rather than always drawing in one direction.
    // This adds extra code but saves the time required to swap the points
    // and adjust for not drawing the end point.

    startX = fx1 << 12;
    startY = fy1 << 12;

    DISPDBG((DBGLVL, "GDI Line %x, %x  deltas %x, %x", 
                     startX, startY, dx, dy));

    if (adx < ady)
    {
        goto y_major;
    }

    if (adx > ady)
    {
        goto x_major;
    }


//slope1_line:

    // All slope 1 lines are sampled in X. i.e. we move the start coord to
    // an integer x and let GLINT truncate in y. This is because all GIQ
    // lines are rounded down in y for values exactly half way between two
    // pixels. If we sampled in y then we would have to round up in x for
    // lines of slope 1 and round down in x for other lines. Sampling in x
    // allows us to use the same GLINT bias in all cases (0x7fff). We do
    // the x round up or down when we move the start point.
 
    if (dx != dy)
    {
        goto slope_minus_1;
    }
    
    if (dx < 0)
    {
        goto slope1_reverse;
    }

    dX = 1 << 16;
    dY = 1 << 16;

    if (IN_S1DIAMOND(m1, n1))
    {
        tmp = (startX + 0x8000) & ~0xffff;
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;
    }
    
    startY += tmp - startX;
    startX = tmp;
    
    if (IN_S1DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x8) & ~0xf;   // nearest integer 
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer 
    }
    
    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

slope1_reverse:
    dX = -1 << 16;
    dY = -1 << 16;
    
    if (IN_S1DIAMOND(m1, n1))
    {
        tmp = (startX + 0x8000) & ~0xffff;
    }
    else
    {
        tmp = startX & ~0xffff;
    }
    
    startY += tmp - startX;
    startX = tmp;
    
    if (IN_S1DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x8) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;            // previous integer 
    }
    
    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

slope_minus_1:
    if (dx < 0)
    {
        goto slope_minus_dx;
    }

    // dx > 0, dy < 0 
    
    dX = 1 << 16;
    dY = -1 << 16;
    
    if (IN_SM1DIAMOND(m1, n1))
    {
        tmp = (startX + 0x7fff) & ~0xffff;
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;
    }
    
    startY += startX - tmp;
    startX = tmp;
    
    if (IN_SM1DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer
    }
    
    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

slope_minus_dx:

    dX = -1 << 16;
    dY = 1 << 16;
    
    if (IN_SM1DIAMOND(m1, n1))
    {
        tmp = (startX + 0x7fff) & ~0xffff;
    }
    else
    {
        tmp = startX & ~0xffff;
    }
    
    startY += startX - tmp;
    startX = tmp;
    
    if (IN_SM1DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;            // previous integer
    }
        
    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

x_major:
    // Dont necessarily render through glint if we are worried
    // about conformance.
    if ((adx > (MAX_LENGTH_CONFORMANT_NONINTEGER_LINES << 4)) &&
        (glintInfo->flags & GLICAP_NT_CONFORMANT_LINES)      &&
        (ady != 0))
    {
        return(FALSE);
    }

    if (dx < 0)
    {
        goto right_to_left_x;
    }

// left_to_right_x:

     // line goes left to right. Round up the start x to an integer
     // coordinate. This is the coord of the first diamond that the
     // line crosses. Adjust start y to match this point on the line.

    dX = 1 << 16;
    
    if (IN_DIAMOND(m1, n1))
    {
        tmp = (startX + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = (startX + 0xffff) & ~0xffff;  // next integer
    }

    // we can optimise for horizontal lines
    if (dy != 0) 
    {
        dY = dy << 16;

        // Need to explicitly round delta down for -ve deltas.
        if (dy < 0)
        {
            dY -= adx - 1;
        }
    
        dY /= adx;
        startY += (((tmp - startX) >> 12) * dY) >> 4;
    }
    else
    {
        dY = 0;
    }
    
    startX = tmp;

    if (IN_DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 = (fx2 + 0xf) & ~0xf;   // next integer
    }
    
    count = (fx2 >> 4) - (startX >> 16);

    goto Draw_Line;

right_to_left_x:

    dX = -1 << 16;
    
    if (IN_DIAMOND(m1, n1))
    {
        tmp = (startX + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = startX & ~0xffff;         // previous integer
    }

    // we can optimise for horizontal lines 
    if (dy != 0) 
    {
        dY = dy << 16;

        // Need to explicitly round delta down for -ve deltas.
        if (dy < 0)
        {
            dY -= adx - 1;
        }
    
        dY /= adx;
        startY += (((startX - tmp) >> 12) * dY) >> 4;
    }
    else
    {
        dY = 0;
    }
    
    startX = tmp;

    if (IN_DIAMOND(m2, n2))
    {
        fx2 = (fx2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fx2 &= ~0xf;               // previous integer
    }
    
    count = (startX >> 16) - (fx2 >> 4);

    goto Draw_Line;

y_major:
    // Dont necessarily render through glint if we are worried
    // about conformance.
    if ((ady > (MAX_LENGTH_CONFORMANT_NONINTEGER_LINES << 4)) &&
        (glintInfo->flags & GLICAP_NT_CONFORMANT_LINES)       &&
        (adx != 0))
    {
        return(FALSE);
    }

    if (dy < 0)
    {
        goto high_to_low_y;
    }

// low_to_high_y:

    dY = 1 << 16;
    
    if (IN_DIAMOND(m1, n1))
    {
        tmp = (startY + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = (startY + 0xffff) & ~0xffff;  // next integer
    }

    // we can optimise for vertical lines
    if (dx != 0) 
    {
        dX = dx << 16;

        // Need to explicitly round delta down for -ve deltas.
        if (dx < 0)
        {
            dX -= ady - 1;
        }
    
        dX /= ady;
        startX += (((tmp - startY) >> 12) * dX) >> 4;
    }
    else
    {
        dX = 0;
    }
    
    startY = tmp;

    if (IN_DIAMOND(m2, n2))
    {
        fy2 = (fy2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fy2 = (fy2 + 0xf) & ~0xf;   // next integer
    }
    
    count = (fy2 >> 4) - (startY >> 16);

    goto Draw_Line;

high_to_low_y:

    dY = -1 << 16;
    
    if (IN_DIAMOND(m1, n1))
    {
        tmp = (startY + 0x7fff) & ~0xffff;  // nearest integer
    }
    else
    {
        tmp = startY & ~0xffff;         // previous integer
    }

    // we can optimise for horizontal lines
    if (dx != 0) 
    {
        dX = dx << 16;

        // Need to explicitly round delta down for -ve deltas.
        if (dx < 0)
        {
            dX -= ady - 1;
        }
    
        dX /= ady;
        startX += (((startY - tmp) >> 12) * dX) >> 4;
    }
    else
    {
        dX = 0;
    }
    
    startY = tmp;

    if (IN_DIAMOND(m2, n2))
    {
        fy2 = (fy2 + 0x7) & ~0xf;   // nearest integer
    }
    else
    {
        fy2 &= ~0xf;            // previous integer
    }
    
    count = (startY >> 16) - (fy2 >> 4);

Draw_Line:
    WAIT_PXRX_DMA_TAGS( 6 );

    QUEUE_PXRX_DMA_TAG( __GlintTagStartXDom,    startX + 0x7fff );
    QUEUE_PXRX_DMA_TAG( __GlintTagStartY,       startY + 0x7fff );
    QUEUE_PXRX_DMA_TAG( __GlintTagdXDom,        dX );
    QUEUE_PXRX_DMA_TAG( __GlintTagdY,           dY );
    QUEUE_PXRX_DMA_TAG( __GlintTagCount,        count );
    QUEUE_PXRX_DMA_TAG( __GlintTagRender,       __RENDER_LINE_PRIMITIVE );

    SEND_PXRX_DMA_BATCH;

    return TRUE;
}