/****************************************************************************
 * Rage 128 Chapter 7 sample code                                           *
 *                                                                          *
 * ov1setup.c - This module performs the higher level setup of the overlay. *
 *                                                                          *
 * Copyright (c) 1999 ATI Technologies Inc. All rights reserved.            *
 ****************************************************************************/
#include <math.h>
#include <stdio.h>
#include "overlay.h"
#include <stdlib.h>

void R128_SetupOverlay (OVERLAY_DESCRIPTOR *OverlayDescriptor, OVERLAY_REG_FIELDS *OverlayRegFields)
{
    /* ********************************************************* */
    /* ** Calculate some basic physical constants for the chip */
    /* ********************************************************* */

    /* Top Level Variables */
    double LeftPixel;
    double RightPixel;
    double LeftUVPixel;
    double RightUVPixel;
    int TopLine;
    int TopUVLine;
    double H_scale_ratio;
    double V_scale_ratio;
    int CRT_V_INC;
    int SourceWidthInPixels;
    int SourceUVWidthInPixels;
    int DisallowFourTapVertFiltering;
    int DisallowFourTapUVVertFiltering;
    int DoNotUseMostRecentlyFetchedLine;
    int SurfaceNum;

    int BytesPerOctWord;
    int LogMemWordsInBytes;
    int MemWordsInBytes;
    int LogTileWidthInMemWords;
    int TileWidthInMemWords;
    int TileWidthInBytes;
    int LogTileHeight;
    int TileHeight;
    int PageSizeInBytes;
    int OV0LB_Rows;

    int BytesPerPixel;      /* Size of a pixel in a plane of the surface */
    int BytesPerUVPixel;

    int HorzUVSubSample;
    int VertUVSubSample;

    BytesPerOctWord = 16;
    LogMemWordsInBytes = 4;
    MemWordsInBytes = 1<<LogMemWordsInBytes;
    LogTileWidthInMemWords = 2;
    TileWidthInMemWords = 1<<LogTileWidthInMemWords;
    TileWidthInBytes = 1<<(LogTileWidthInMemWords+LogMemWordsInBytes);
    LogTileHeight = 4;
    TileHeight = 1<<LogTileHeight;
    PageSizeInBytes = 64*MemWordsInBytes;
    OV0LB_Rows = 96;

    switch (OverlayDescriptor->SURFACE_FORMAT)
    {
        case 3:
        case 4:
        case 11:
        case 12:    BytesPerPixel = 2;
                    break;
        case 6:     BytesPerPixel = 4;
                    break;
        case 9:
        case 10:
        case 13:
        case 14:    BytesPerPixel = 1;
                    break;
        default:    /*insert a debug statement here. */
                    break;
    }

    switch (OverlayDescriptor->SURFACE_FORMAT)
    {
        case 3:
        case 4:     BytesPerUVPixel = 0;
                    break;/* In RGB modes, the BytesPerUVPixel is don't care */
        case 11:
        case 12:    BytesPerUVPixel = 2;
                    break;
        case 6:     BytesPerUVPixel = 0;
                    break;      /* In RGB modes, the BytesPerUVPixel is don't care */
        case 9:
        case 10:    BytesPerUVPixel = 1;
                    break;
        case 13:
        case 14:    BytesPerUVPixel = 2;
                    break;
        default:    /* insert a debug statement here. */
                    break;

    }

    switch (OverlayDescriptor->SURFACE_FORMAT)
    {
        case 3:
        case 4:
        case 6:     HorzUVSubSample = 1;
                    break;
        case 9:     HorzUVSubSample = 4;
                    break;
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:    HorzUVSubSample = 2;
                    break;
        default:    /* insert debug statement here. */
                    break;
    }

    switch (OverlayDescriptor->SURFACE_FORMAT)
    {
        case 3:
        case 4:
        case 6:
        case 11:
        case 12:    VertUVSubSample = 1;
                    break;
        case 9:     VertUVSubSample = 4;
                    break;
        case 10:
        case 13:
        case 14:    VertUVSubSample = 2;
                    break;
        default:    /* insert debug statment here. */
                    break;
    }

    /* ********************************************************* */
    /* ** Determine Left and Right pixles */
    /* ********************************************************* */

    /* Warning: rSrc values are in floating point, thus if your thinking in terms of */
    /* pixels rSrcRight and rSrcBottom may need a +1 factor in order to bound the source region properly. */
    /* Example 1: Four pixels (0 to 3) are depected below. We want to display pixels 1 and 2. Thus we */
    /* bound the region be defining rSrcRight to be 1.0 and rSrcLeft to be 3.0. */
    /* 0000000000111111111122222222223333333333 */
    /*           \                     / */
    /*           1.0                3.0 */
    /* */
    /* Example 2: Four pixels (0 to 3) are depected below. We want to display a sub region that is three pixels */
    /* wide starting half way through pixel 0. Thus we */
    /* bound the region be defining rSrcRight to be 0.5 and rSrcLeft to be 3.5. */
    /* 0000000000111111111122222222223333333333 */
    /*      \                            / */
    /*      0.5                        3.5 */
    /* */

    if (floor(OverlayDescriptor->PHYSICAL_SrcLeft)<0)
    {
        LeftPixel = 0;
    }
    else
    {
        LeftPixel = (int)floor(OverlayDescriptor->PHYSICAL_SrcLeft);
    }
    /* Round rSrcBottom up and subtract one */
    if (ceil(OverlayDescriptor->PHYSICAL_SrcRight) > OverlayDescriptor->SrcWidth)
    {
        RightPixel = OverlayDescriptor->SrcWidth - 1;
    }
    else
    {
        RightPixel = (int)ceil(OverlayDescriptor->PHYSICAL_SrcRight) - 1;
    }

    if (floor(OverlayDescriptor->PHYSICAL_SrcLeft/HorzUVSubSample)<0)
    {
        LeftUVPixel = 0;
    }
    else
    {
        LeftUVPixel = (int)floor(OverlayDescriptor->PHYSICAL_SrcLeft/HorzUVSubSample);
    }
    /* Round rSrcBottom up and subtract one */
    if (ceil(OverlayDescriptor->PHYSICAL_SrcRight/HorzUVSubSample) > OverlayDescriptor->SrcWidth/HorzUVSubSample)
    {
        RightUVPixel = OverlayDescriptor->SrcWidth/HorzUVSubSample - 1;
    }
    else
    {
        RightUVPixel = (int)ceil(OverlayDescriptor->PHYSICAL_SrcRight/HorzUVSubSample) - 1;
    }

    /* ********************************************************* */
    /* ** Setup Black Bordering */
    /* ********************************************************* */

    {
        int BottomLine;
        int SourceLinesUsed;
        int BottomUVLine;
        int SourceUVLinesUsed;
        double tempBLANK_LINES_AT_TOP;

        if (floor(OverlayDescriptor->PHYSICAL_SrcTop)<0) {
            tempBLANK_LINES_AT_TOP = -floor(OverlayDescriptor->PHYSICAL_SrcTop);
            TopLine = 0;
        }
        else {
            tempBLANK_LINES_AT_TOP = 0;
            TopLine = (int)floor(OverlayDescriptor->PHYSICAL_SrcTop);
        }
        /* Round rSrcBottom up and subtract one */
        if (ceil(OverlayDescriptor->PHYSICAL_SrcBottom) > OverlayDescriptor->SrcHeight)
        {
            BottomLine = OverlayDescriptor->SrcHeight - 1;
        }
        else
        {
            BottomLine = (int)ceil(OverlayDescriptor->PHYSICAL_SrcBottom) - 1;
        }

        if (BottomLine >= TopLine)
        {
            SourceLinesUsed = BottomLine - TopLine + 1;
        }
        else
        {
            /*CYCACC_ASSERT(0, "SourceLinesUsed less than or equal to zero.") */
            SourceLinesUsed = 1;
        }

          {
            int SourceHeightInPixels;
            SourceHeightInPixels = BottomLine - TopLine + 1;

        }

        OverlayRegFields->val_OV0_P1_ACTIVE_LINES_M1 = SourceLinesUsed - 1;
        OverlayRegFields->val_OV0_P1_BLNK_LN_AT_TOP_M1 = ((int)tempBLANK_LINES_AT_TOP-1) & 0xfff;

        TopUVLine = ((int)(OverlayDescriptor->PHYSICAL_SrcTop/VertUVSubSample) < 0)  ?  0: (int)(OverlayDescriptor->PHYSICAL_SrcTop/VertUVSubSample);   /* Round rSrcTop down */
        BottomUVLine = (ceil((OverlayDescriptor->PHYSICAL_SrcBottom/VertUVSubSample)) > (OverlayDescriptor->SrcHeight/VertUVSubSample))
        ? (OverlayDescriptor->SrcHeight/VertUVSubSample) - 1: (int)ceil((OverlayDescriptor->PHYSICAL_SrcBottom/VertUVSubSample)) -1;

        if (BottomUVLine >= TopUVLine)
        {
            SourceUVLinesUsed = BottomUVLine - TopUVLine + 1;
        }
        else
        {
            /*CYCACC_ASSERT(0, "SourceUVLinesUsed less than or equal to zero.") */
            SourceUVLinesUsed = 1;
        }
        OverlayRegFields->val_OV0_P23_ACTIVE_LINES_M1 = SourceUVLinesUsed - 1;
        OverlayRegFields->val_OV0_P23_BLNK_LN_AT_TOP_M1 = ((int)(tempBLANK_LINES_AT_TOP/VertUVSubSample)-1) & 0x7ff;
    }

    /* ********************************************************* */
    /* ** Determine Physical Horizontal Scaling Ratios */
    /* ********************************************************* */

    {
        double SCREEN_Width;
        int HorzReplicationFactor;

        SCREEN_Width = OverlayDescriptor->PHYSICAL_SrcRight - OverlayDescriptor->PHYSICAL_SrcLeft;

        /* If the display's pixel clock is too high, the scaler hardware can't keep up. */
        /* In this case the scaler's clock (called ECP) is divided down by 2 or by 4. */
        /* (The BIOS makes this decision, so query the BIOS to find out what */
        /* ECP_DIV_ValueFromPLLRegs is). Each video pixel that the scaler generates is */
        /* then replicated either two or four times on the display. This imposes a rule */
        /* on the destination window right coordinate which is checked below. It also */
        /* affects the horizontal scale ratio that is programmed into the video scaler. */
        /*CYCACC_ASSERT((OverlayDescriptor->ECP_DIV_ValueFromPLLRegs==0) || (OverlayDescriptor->ECP_DIV_ValueFromPLLRegs==1) || (OverlayDescriptor->ECP_DIV_ValueFromPLLRegs==2), "Illegal value for ECP_DIV_ValueFromPLLRegs") */
        HorzReplicationFactor = 1 << OverlayDescriptor->ECP_DIV_ValueFromPLLRegs;

        if ( ((OverlayDescriptor->ECP_DIV_ValueFromPLLRegs==1)||
                  (OverlayDescriptor->ECP_DIV_ValueFromPLLRegs==2)) &&
        ((OverlayDescriptor->PHYSICAL_DestRight - OverlayDescriptor->PHYSICAL_DestLeft + 1) % HorzReplicationFactor != 0) )
        {
            /* Warning, there is no check to see if the value below exceeds the display's */
            /* horizontal resolution! This check may be more complicated that it appears. */
            OverlayDescriptor->PHYSICAL_DestRight = OverlayDescriptor->PHYSICAL_DestLeft +
                                        (int)ceil((OverlayDescriptor->PHYSICAL_DestRight -
                                        OverlayDescriptor->PHYSICAL_DestLeft + 1) /
                                        HorzReplicationFactor) * HorzReplicationFactor - 1;
        }

        /*Adjust the scale ratios to map the source window to the destination window. */
        H_scale_ratio = (double)ceil((OverlayDescriptor->PHYSICAL_DestRight -
                        OverlayDescriptor->PHYSICAL_DestLeft + 1) /
                        HorzReplicationFactor) / SCREEN_Width;
    }

    /* Top, Bottom and Right Crops can be out of range. The driver will program the hardware    // to create a black border at the top and bottom. This is useful for DVD letterboxing. */
    SourceWidthInPixels = (int)(RightPixel - LeftPixel + 1);
    SourceUVWidthInPixels = (int)(RightUVPixel - LeftUVPixel + 1);

    /* Top Cropping (Positive Crop Values) is achieved by adjusting the Base Address by the     // required number of pitches. */
    /* Left Cropping is achieved by adjusting the Base Address by the required number of    // Octwords. Sub Octword accurate positioning is achieved by using the */
    /* iOV0_VID_BUF?_START_PIX field to point to the desired start pixel inside the */
    /* first octword of each line. Note that the pitch must be Octword alligned. */
    /* For tiled surfaces, the base address is harder to adjust, and in addition, the five */
    /* LSB's of the first line number must be provided as well. Before the tiled bit can be */
    /* set, software must insure that the surface is a legal tiled surface. */
    /* constraining pitch and base offset (base address). */

    /* ** The following code checks that the image width does not exceed the length supported */
    /* by the line buffer and determines whether to allow or disallow Four Tap Filtering. */
    /* */
    DisallowFourTapVertFiltering = 0;       /* Allow it by default */
    DisallowFourTapUVVertFiltering = 0;     /* Allow it by default */

    {
        int SourceWidthInMemWords;
        int SourceUVWidthInMemWords;

        SourceWidthInMemWords = (int)(ceil(RightPixel*BytesPerPixel / MemWordsInBytes) -
                                floor(LeftPixel*BytesPerPixel / MemWordsInBytes) + 1);
        /* SourceUVWidthInMemWords means Source_U_or_V_or_UV_WidthInMemWords depending on whether the UV is packed together of not. */
        SourceUVWidthInMemWords = (int)(ceil(RightUVPixel*BytesPerUVPixel /
                                  MemWordsInBytes) - floor(LeftUVPixel*BytesPerUVPixel /
                                  MemWordsInBytes) + 1);

        switch (OverlayDescriptor->SURFACE_FORMAT)
        {
            case 9:
            case 10:    if ((ceil(SourceWidthInMemWords/2)-1) * 2 > (signed)OV0LB_Rows-1)
                        {
                            /* insert debug statement here.  Illegal value. */
                        }
                        else if ((SourceWidthInMemWords-1) * 2 > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapVertFiltering = 1;
                        }

                        if ((ceil(SourceUVWidthInMemWords/2)-1) * 4 + 1 > (signed)OV0LB_Rows-1)
                        {
                            /*CYCACC_ASSERT(0, "Image U plane width spans more octwords than supported by hardware.") */
                        }
                        else if ((SourceUVWidthInMemWords-1) * 4 + 1 > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapUVVertFiltering = 1;
                        }

                        if ((ceil(SourceUVWidthInMemWords/2)-1) * 4 + 3 > (signed)OV0LB_Rows-1)
                        {
                            /*CYCACC_ASSERT(0, "Image V plane width spans more octwords than supported by hardware.") */
                        }
                        else if ((SourceUVWidthInMemWords-1) * 4 + 3 > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapUVVertFiltering = 1;
                        }
                        break;
            case 13:
            case 14:    if ((ceil(SourceWidthInMemWords/2)-1) * 2 > (signed)OV0LB_Rows-1)
                        {
                            /* insert debug statement here. Illegal value. */
                        }
                        else if ((SourceWidthInMemWords-1) * 2 > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapVertFiltering = 1;
                        }

                        if ((ceil(SourceUVWidthInMemWords/2)-1) * 2 + 1 > (signed)OV0LB_Rows-1)
                        {
                            /*CYCACC_ASSERT(0, "Image UV plane width spans more octwords than supported by hardware.") */
                        }
                        else if ((SourceUVWidthInMemWords-1) * 2 + 1 > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapUVVertFiltering = 1;
                        }
                        break;
            case 3:
            case 4:
            case 6:
            case 11:
            case 12:    if ((ceil(SourceWidthInMemWords/2)-1) > (signed)OV0LB_Rows-1)
                        {
                            /* insert debug statement here. Illegal value. */
                        }
                        else if ((SourceWidthInMemWords-1) > (signed)OV0LB_Rows-1)
                        {
                            DisallowFourTapVertFiltering = 1;
                        }
                        break;
            default:    /* insert debug statement here. */
                        break;
        }
    }

    /* ********************************************************* */
    /* ** Derive a detailed surface description from the simple */
    /* ** one if a detailed description is not provided */
    /* ********************************************************* */
    if (!OverlayDescriptor->USE_DETAILED_SURFACE_DESCRIPTION)
    {
        /* Then fill out the detailed description based on Microsofts standard */
        OverlayDescriptor->SurfacePitch[0] = OverlayDescriptor->SimpleSurfacePitch;
        OverlayDescriptor->SurfacePitch[1] = OverlayDescriptor->SimpleSurfacePitch;
        for (SurfaceNum=0; SurfaceNum<6; SurfaceNum++)
        {
            OverlayDescriptor->PitchSelectOfSurface[SurfaceNum] = 0;
            OverlayDescriptor->BaseOffsetOfSurface[SurfaceNum] = OverlayDescriptor->SimpleBaseOffsetOfSurface;
        }

        switch (OverlayDescriptor->SURFACE_FORMAT)
        {
            case 9:
            case 10:    /* Use Microsofts "Y over (V beside U)" planer format. */
                    OverlayDescriptor->SurfaceSpecificXOffset[0] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[1] = OverlayDescriptor->SimpleSurfacePitch/2;
                    OverlayDescriptor->SurfaceSpecificXOffset[2] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[3] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[4] = OverlayDescriptor->SimpleSurfacePitch/2;
                    OverlayDescriptor->SurfaceSpecificXOffset[5] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[0] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[1] = OverlayDescriptor->SrcHeight;
                    OverlayDescriptor->SurfaceSpecificYOffset[2] = OverlayDescriptor->SrcHeight;
                    OverlayDescriptor->SurfaceSpecificYOffset[3] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[4] = OverlayDescriptor->SrcHeight;
                    OverlayDescriptor->SurfaceSpecificYOffset[5] = OverlayDescriptor->SrcHeight;
                    break;
            case 13:
            case 14:    /* Use Microsofts "Y over (V beside U)" planer format. */
                    OverlayDescriptor->SurfaceSpecificXOffset[0] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[1] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[2] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[3] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[4] = 0;
                    OverlayDescriptor->SurfaceSpecificXOffset[5] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[0] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[1] = OverlayDescriptor->SrcHeight;
                    OverlayDescriptor->SurfaceSpecificYOffset[2] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[3] = 0;
                    OverlayDescriptor->SurfaceSpecificYOffset[4] = OverlayDescriptor->SrcHeight;
                    OverlayDescriptor->SurfaceSpecificYOffset[5] = 0;
                    break;
            case 3:
            case 4:
            case 6:
            case 11:
            case 12:    for (SurfaceNum=0; SurfaceNum<6; SurfaceNum++)
                        {
                            OverlayDescriptor->SurfaceSpecificXOffset[SurfaceNum] = 0;
                            OverlayDescriptor->SurfaceSpecificYOffset[SurfaceNum] = 0;
                        }
                        break;
            default:    /* insert debug statement here. */
                        break;
        }
    }

    /* ********************************************************* */
    /* ** Determine in which Octword the surface starts, */
    /* ** and Program the Base Address Registers */
    /* ********************************************************* */
    {
        int FiveLSBsOfTheYIfTiled[6];
        int TopLeftMemWordOfSurface[6];
        int UVSurfaceMask;

        /* ToDo_Active: We need to determine how best to arrange buffers for Planer, Packed Even/Odd Field Double Bufferd Surfaces Etc. */
        for (SurfaceNum=0; SurfaceNum<6; SurfaceNum++)
        {
            int PitchOfSurface[6];
            int X, Y;
            int TopLeftByte;

            PitchOfSurface[SurfaceNum] = OverlayDescriptor->SurfacePitch[OverlayDescriptor->PitchSelectOfSurface[SurfaceNum]];

            /* The following case statement sets six flags that indicate if a given             // surface is used for UV */
            switch (OverlayDescriptor->SURFACE_FORMAT)
            {
                case 3:
                case 4:
                case 11:
                case 12:
                case 6:     UVSurfaceMask = 0;
                            break;
                case 9:
                case 10:    UVSurfaceMask = 0x36;
                            break;  /* 110110 */
                case 13:
                case 14:    UVSurfaceMask = 0x12;
                            break;  /* 010010 */
                default:    /* insert debug statement here. */
                            break;
            }

            /* X is in Bytes, Y is in Lines */
            if ((UVSurfaceMask>>SurfaceNum) & 1)
            {
                /* This is a U, V, or Packed UV surface */
                X = OverlayDescriptor->SurfaceSpecificXOffset[SurfaceNum] + ((int)LeftUVPixel * BytesPerUVPixel);
                Y = OverlayDescriptor->SurfaceSpecificYOffset[SurfaceNum] + (int)TopUVLine;
            }
            else
            {
                X = OverlayDescriptor->SurfaceSpecificXOffset[SurfaceNum] + ((int)LeftPixel * BytesPerPixel);
                Y = OverlayDescriptor->SurfaceSpecificYOffset[SurfaceNum] + (int)TopLine;
            }
            if (OverlayDescriptor->SurfaceIsTiled)
            {
                TopLeftByte = OverlayDescriptor->BaseOffsetOfSurface[SurfaceNum] + (int)(Y/TileHeight)*PitchOfSurface[SurfaceNum]*TileHeight + (int)(X/TileWidthInBytes)*TileHeight*TileWidthInBytes + (Y%TileHeight)*TileWidthInBytes + (X%TileWidthInBytes);
                FiveLSBsOfTheYIfTiled[SurfaceNum] = Y % (TileHeight*2);
            }
            else
            {

                TopLeftByte = OverlayDescriptor->BaseOffsetOfSurface[SurfaceNum] + Y * PitchOfSurface[SurfaceNum] + X;
                FiveLSBsOfTheYIfTiled[SurfaceNum] = 0;
            }

            TopLeftMemWordOfSurface[SurfaceNum] = (int)(TopLeftByte / MemWordsInBytes);
        }

        OverlayRegFields->val_OV0_BUF0_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[0];
        OverlayRegFields->val_OV0_BUF1_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[1];
        OverlayRegFields->val_OV0_BUF2_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[2];
        OverlayRegFields->val_OV0_BUF3_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[3];
        OverlayRegFields->val_OV0_BUF4_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[4];
        OverlayRegFields->val_OV0_BUF5_1ST_LINE_LSBS = FiveLSBsOfTheYIfTiled[5];

        OverlayRegFields->val_OV0_VID_BUF0_BASE_ADRS = TopLeftMemWordOfSurface[0];
        OverlayRegFields->val_OV0_VID_BUF1_BASE_ADRS = TopLeftMemWordOfSurface[1];
        OverlayRegFields->val_OV0_VID_BUF2_BASE_ADRS = TopLeftMemWordOfSurface[2];
        OverlayRegFields->val_OV0_VID_BUF3_BASE_ADRS = TopLeftMemWordOfSurface[3];
        OverlayRegFields->val_OV0_VID_BUF4_BASE_ADRS = TopLeftMemWordOfSurface[4];
        OverlayRegFields->val_OV0_VID_BUF5_BASE_ADRS = TopLeftMemWordOfSurface[5];

        OverlayRegFields->val_OV0_VID_BUF0_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[0];
        OverlayRegFields->val_OV0_VID_BUF1_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[1];
        OverlayRegFields->val_OV0_VID_BUF2_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[2];
        OverlayRegFields->val_OV0_VID_BUF3_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[3];
        OverlayRegFields->val_OV0_VID_BUF4_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[4];
        OverlayRegFields->val_OV0_VID_BUF5_PITCH_SEL = OverlayDescriptor->PitchSelectOfSurface[5];

        OverlayRegFields->val_OV0_VID_BUF0_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
        OverlayRegFields->val_OV0_VID_BUF1_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
        OverlayRegFields->val_OV0_VID_BUF2_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
        OverlayRegFields->val_OV0_VID_BUF3_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
        OverlayRegFields->val_OV0_VID_BUF4_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
        OverlayRegFields->val_OV0_VID_BUF5_TILED_ADRS = OverlayDescriptor->SurfaceIsTiled;
    }

    /* ********************************************************* */
    /* ** Calculate the availible bandwidth. */
    /* ********************************************************* */

    /* First we need to determine how many OctWords are fectched for each plane. */

    {
        double XCLKPeriodInNSec;
        double VCLKPeriodInNSec;
        double ECPPeriodInNSec;
        DWORD ECP_DIV_ValueFromPLLRegsideDownFactor;

        DWORD EarliestDataTransfer;
        DWORD LatestDataTransfer;
        DWORD VCLK_Offset;

        DWORD TotalScalerHBlankInVCLKs;
        DWORD TotalScalerHBlankInXCLKs;
        DWORD TotalDisplayHBlankInVCLKs;
        DWORD TotalDisplayHBlankInXCLKs;
        signed long XCLKsLeftInScalerHBlank;
        DWORD NumberOfScalerBlankAccesses;

        double InputRate;
        double OutputRate;
        double FillRate;

        DWORD BytesFetchedAtEndOfLine;
        DWORD LongFillTimeInXCLKs;
        double LongestFillTime;
        double BytesFetchedDuringLongestFill;
        double RemainingFillInXCLKs;
        double RemainingBytes;
        DWORD DifferentialLatencyInXCLKs;
        DWORD MaxLatencyInXClks;
        DWORD MinLatencyInXClks;
        double FIFOLevelAtMinBurstStart;
        double ShortFillTime;
        double BytesInShortFill;
        DWORD NumShortFills;
        DWORD ShortestDrainTimeInVCLKs;
        DWORD BytesFetchedAtBeginningOfLine;

        DWORD HardwareCursorXCLKs;

        DWORD PrimaryDisplayWidth;
        DWORD BytesInDisplayLine;
        DWORD DisplayFIFOPrefillInBytes;
        DWORD DisplayFIFOPrefillInOctWords;
        DWORD DisplayFIFOPrefillXCLKs;

        double DataFetchedInScalerHBlank;
        DWORD NumDispAccessInScalerHBlank;

        DWORD SubPictureXCLKs;

        DWORD NumberVideoCaptures;
        DWORD CaptureOctwordsWritten;
        double CaptureDataRate;
        double DataCapturedInScalerHBlank;
        DWORD NumCapAccessInScalerHBlank;

        DWORD CaptureXCLKs;

        /* XCLK is the internal memory clock. It will be half the speed of the external */
        /* memory clock for 64bit SDR (Single Data Rate) memory systems. */
        XCLKPeriodInNSec = (double)OverlayDescriptor->MemoryClock_XCLK_PeriodInPicoSec / 1000;
        VCLKPeriodInNSec = (double)OverlayDescriptor->PixelClock_VCLK_PeriodInPicoSec / 1000;

        /* ECP is the name of the clock that the scaler runs on. It will be 1.0 or 0.5, */
        /* or 0.25 of VCLK (the pixel clock). */
        switch (OverlayDescriptor->ECP_DIV_ValueFromPLLRegs)
        {
            case 0:     ECP_DIV_ValueFromPLLRegsideDownFactor = 1;
                        break;
            case 1:     ECP_DIV_ValueFromPLLRegsideDownFactor = 2;
                        break;
            case 2:     ECP_DIV_ValueFromPLLRegsideDownFactor = 4;
                        break;
            default:    /* This is an illegal value. */
                        break;
        }

        ECPPeriodInNSec = VCLKPeriodInNSec * ECP_DIV_ValueFromPLLRegsideDownFactor;
        if (ECPPeriodInNSec < 8.0)
        {
            /*wsprintf(MsgString, "Error: "__FILE__": Illegal ECPPeriodInNSec (%ld ps). */
            /*This clock must not be below 8000 picoseconds.", */
            /*(int)(ECPPeriodInNSec*1000)); */

        }

        /* Calculate how much time there is for reading in lines. */
        /* First we determine when data transfers to the scaler can start. */

        /* Need OverlayRegFields->val_OV0_P1_X_END and OverlayRegFields->val_OV0_P2_X_END for this function!! */

        /* Temporary Code!! Remove !! */
        OverlayRegFields->val_OV0_P1_X_END = (DWORD)(OverlayDescriptor->PHYSICAL_SrcRight -
                                             OverlayDescriptor->PHYSICAL_SrcLeft + 1);
        switch (OverlayDescriptor->SURFACE_FORMAT)
        {
            case 9:     OverlayRegFields->val_OV0_P2_X_END = (DWORD)(OverlayDescriptor->PHYSICAL_SrcRight - OverlayDescriptor->PHYSICAL_SrcLeft + 1) / 4;
                        break;
            case 10:    OverlayRegFields->val_OV0_P2_X_END = (DWORD)(OverlayDescriptor->PHYSICAL_SrcRight - OverlayDescriptor->PHYSICAL_SrcLeft + 1) / 2;
                        break;
            default:    OverlayRegFields->val_OV0_P2_X_END = 0;
                        break;
        }
        /* End temporary code */

        CalcScalerHBlank (
            (OverlayDescriptor->SURFACE_FORMAT),
            (OverlayDescriptor->PHYSICAL_DestRight),
            (OverlayDescriptor->PHYSICAL_DestLeft),
            (OverlayDescriptor->PrimaryPixelDepthInBytes),
            (OverlayDescriptor->HTOTAL_InCharactersMinusOne),
            (OverlayRegFields->val_OV0_P1_X_END),
            (OverlayRegFields->val_OV0_P2_X_END),

            XCLKPeriodInNSec,
            VCLKPeriodInNSec,
            ECPPeriodInNSec,
            ECP_DIV_ValueFromPLLRegsideDownFactor,

            &EarliestDataTransfer,
            &LatestDataTransfer,
            &VCLK_Offset);

        TotalScalerHBlankInVCLKs = LatestDataTransfer - EarliestDataTransfer + 1;
        TotalScalerHBlankInXCLKs = (DWORD)(TotalScalerHBlankInVCLKs * VCLKPeriodInNSec /
                                    XCLKPeriodInNSec);

        TotalDisplayHBlankInVCLKs = (OverlayDescriptor->HTOTAL_InCharactersMinusOne -
                                    OverlayDescriptor->HDISP_InCharactersMinusOne) * 8;
        TotalDisplayHBlankInXCLKs = (DWORD)(TotalDisplayHBlankInVCLKs * VCLKPeriodInNSec /
                                    XCLKPeriodInNSec);

        XCLKsLeftInScalerHBlank = TotalScalerHBlankInXCLKs;

        /* Start subtracting off cycles used by other higher priority clients: */
        /*      Display - Hardware Cursor */
        /*      Display - FIFO Prefill */
        /*      Display - FIFO Refill */
        /*      Subpicture */
        /*      Video Capture Ports */
        /*      Refresh */

        /*      Display - Hardware Cursor (Requests one octword - memory controler may take two octwords. */
        HardwareCursorXCLKs = OverlayDescriptor->OpenPenalty + (1+1) *
                              OverlayDescriptor->CyclesPerOctword +
                              OverlayDescriptor->ReadClosePenalty;
        XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - HardwareCursorXCLKs;
        NumberOfScalerBlankAccesses = 1;

        /*      Display - FIFO Prefill */
        PrimaryDisplayWidth = (OverlayDescriptor->HDISP_InCharactersMinusOne + 1) * 8;
        BytesInDisplayLine = PrimaryDisplayWidth *
                             OverlayDescriptor->PrimaryPixelDepthInBytes;
        if (BytesInDisplayLine > OverlayDescriptor->DisplayFIFOSizeInBytes)
        {
            DisplayFIFOPrefillInBytes = OverlayDescriptor->DisplayFIFOSizeInBytes;
        }
        else
        {
            DisplayFIFOPrefillInBytes = (int)(ceil((double)BytesInDisplayLine / 64) * 64);      /* Rounded up to the nearest Burst of four */
        }

        DisplayFIFOPrefillInOctWords = DisplayFIFOPrefillInBytes/BytesPerOctWord;
        if (OverlayDescriptor->DISP_LINES_ARE_64BYTE_ALLIGNED)
        {
            /* This assumes that every display line starts on a 64 Byte boundary. */
            DisplayFIFOPrefillXCLKs = OverlayDescriptor->OpenPenalty + DisplayFIFOPrefillInOctWords*OverlayDescriptor->CyclesPerOctword + OverlayDescriptor->ReadClosePenalty;
        }
        else
        {
            /* Open, only the last octword of the the first burst contains usefull data. The remaining Octwords take "ceil(...)*4" transfers. Then there is a close. */
            DisplayFIFOPrefillXCLKs = OverlayDescriptor->OpenPenalty + (4 + (int)ceil((double)(DisplayFIFOPrefillInOctWords - 1)/4)*4) * OverlayDescriptor->CyclesPerOctword + OverlayDescriptor->ReadClosePenalty;
        }

        XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - DisplayFIFOPrefillXCLKs;
        NumberOfScalerBlankAccesses = NumberOfScalerBlankAccesses + 0;  /* The display FIFO fill always follows on the heels of the Hardware Cursor fetch. */

        /*      Display - FIFO Refill */

        /* First check to see if there is any data left to be fetched after the FIFO prefill. */
        if (BytesInDisplayLine > OverlayDescriptor->DisplayFIFOSizeInBytes)
        {
            /* Next assume worst case display behaviour between EarliestDataTransfer and the last display fetch. */
            /* Worst case occurs when a display transfer into an empty FIFO starts at "EarliestDataTransfer", and fills untill the display */
            /* FIFO contains enough data to finish the line, or is full to the top. */

            InputRate = BytesPerOctWord / OverlayDescriptor->CyclesPerOctword / XCLKPeriodInNSec;
            OutputRate = OverlayDescriptor->PrimaryPixelDepthInBytes / VCLKPeriodInNSec;
            /* When filling, the display is also draining, thus the fill rate is the difference between InputRate and OutputRate. */
            FillRate = InputRate - OutputRate;

            BytesFetchedAtEndOfLine = (PrimaryDisplayWidth - EarliestDataTransfer) * OverlayDescriptor->PrimaryPixelDepthInBytes;
            /* Check that there are at least that many bytes left to fetch. */
            if (BytesFetchedAtEndOfLine >= BytesInDisplayLine - OverlayDescriptor->DisplayFIFOSizeInBytes)
            {
                BytesFetchedAtEndOfLine = BytesInDisplayLine - OverlayDescriptor->DisplayFIFOSizeInBytes;
                LongFillTimeInXCLKs = (int)ceil(BytesFetchedAtEndOfLine / InputRate / XCLKPeriodInNSec);
                XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - (OverlayDescriptor->OpenPenalty + LongFillTimeInXCLKs + OverlayDescriptor->ReadClosePenalty);
                NumDispAccessInScalerHBlank = 1;
            }
            else
            {
                /* BytesFetchedAtEndOfLine is unchanged */
                /* The longest fill is limited by the FIFO size. */
                LongestFillTime = ((OverlayDescriptor->DisplayFIFOSizeInBytes-4*BytesPerOctWord) / FillRate);
                BytesFetchedDuringLongestFill = InputRate * LongestFillTime;

                /* Some calculations that we will need later */
                MaxLatencyInXClks = 28;     /* This is a rough approximation - applies to the display only! */
                MinLatencyInXClks = 10;     /* This is reasonably accurate */
                DifferentialLatencyInXCLKs = MaxLatencyInXClks - MinLatencyInXClks;
                FIFOLevelAtMinBurstStart = OverlayDescriptor->DisplayFIFOSizeInBytes - DifferentialLatencyInXCLKs * BytesPerOctWord;
                ShortFillTime = FIFOLevelAtMinBurstStart / FillRate;
                BytesInShortFill = ShortFillTime * InputRate;

                if (BytesFetchedDuringLongestFill > BytesFetchedAtEndOfLine)
                {
                    LongFillTimeInXCLKs = (int)ceil(BytesFetchedAtEndOfLine / InputRate / XCLKPeriodInNSec);
                    XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - (OverlayDescriptor->OpenPenalty + LongFillTimeInXCLKs + OverlayDescriptor->ReadClosePenalty);
                    RemainingFillInXCLKs = 0;
                    NumDispAccessInScalerHBlank = 1;
                }
                else
                {
                    LongFillTimeInXCLKs = (int)ceil(LongestFillTime / XCLKPeriodInNSec);
                    XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - (OverlayDescriptor->OpenPenalty + LongFillTimeInXCLKs + OverlayDescriptor->ReadClosePenalty);
                    RemainingBytes = BytesFetchedAtEndOfLine - InputRate * LongestFillTime;
                    /* In the worst case, these remaining bytes will be fetched with a large number of minimum size bursts. */
                    NumShortFills = (int)ceil(RemainingBytes / BytesInShortFill);
                    NumDispAccessInScalerHBlank = 1 + NumShortFills;

                    XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - (DWORD)(RemainingBytes / InputRate / XCLKPeriodInNSec) - NumShortFills * (OverlayDescriptor->OpenPenalty + OverlayDescriptor->ReadClosePenalty);
                }

                /* OK, we've accounted for all of the display fetches at the end of the                 // line (i.e. the beginning of the scaler blank). */
                /* There may still be some fetches at the beginning of the display line                 // (i.e. the end of the scaler blank) if the scaler */
                /* doesn't start at the left edge of the display. These remaining               // fetches fall into two categories. */
                /* a) fetches that may prevent the scaler from getting the data it needs                //    to get started, and */
                /* b) fetches that may prevent the scaler from getting the data it needs                //    to sustain the display of a line. */
                /* Calculate (a) first. */
                ShortestDrainTimeInVCLKs = (DWORD)(FIFOLevelAtMinBurstStart /
                                            OutputRate / VCLKPeriodInNSec);
                BytesFetchedAtBeginningOfLine = ((OverlayDescriptor->HTOTAL_InCharactersMinusOne+1)
                                                * 8 + ShortestDrainTimeInVCLKs - LatestDataTransfer)
                                                 * OverlayDescriptor->PrimaryPixelDepthInBytes;

                if (BytesFetchedAtBeginningOfLine > 0)
                {
                    /* Let's assume the worst case behaviour is that the display FIFO */
                    /* always get's topped off as the earliest possible time. This */
                    /* results in the maximum number of small display bursts during active region */
                    /* as well. */
                    NumShortFills = (int)ceil(BytesFetchedAtBeginningOfLine / BytesInShortFill);
                    NumDispAccessInScalerHBlank = NumDispAccessInScalerHBlank + NumShortFills;

                    XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - (DWORD)(BytesFetchedAtBeginningOfLine /
                                        InputRate / XCLKPeriodInNSec) - NumShortFills *
                      (OverlayDescriptor->OpenPenalty + OverlayDescriptor->ReadClosePenalty);
                }

            }
        }
        else
        {
            DataFetchedInScalerHBlank = 0;
            NumDispAccessInScalerHBlank = 0;
        }
        NumberOfScalerBlankAccesses = NumberOfScalerBlankAccesses + NumDispAccessInScalerHBlank;

        /*      Subpicture */
        if (!OverlayDescriptor->CAN_GUARANTEE_NO_SUBPICTURE)
        {
            /* If the subpicture is on, then there are two separate fetches. */
            /* SUBPICTURE SOFTWARE MUST GUARENTEE THAT BOTH FETCHES ARE 64 */
            /* BYTE ALLIGNED. */
            /* The second 6 cycle miss penalty can in theory be avoided by concatinating */
            /* the two portions of a subpicture line. */
            SubPictureXCLKs = (OverlayDescriptor->OpenPenalty +
                              3 * 4 * OverlayDescriptor->CyclesPerOctword +
                              OverlayDescriptor->ReadClosePenalty) +
                              (OverlayDescriptor->OpenPenalty +
                              1 * 4 * OverlayDescriptor->CyclesPerOctword +
                              OverlayDescriptor->ReadClosePenalty);
            XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - SubPictureXCLKs;
            NumberOfScalerBlankAccesses = NumberOfScalerBlankAccesses + 1;
        }

        /*      Video Capture Ports */
        if (OverlayDescriptor->CAN_GUARANTEE_NO_VIDEO_CAPTURE)
        {
            /* Subtract nothing */
            XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - 0;
        }
        else
        {
            /* At least one video capture engine is on. */
            if (OverlayDescriptor->CAN_GUARANTEE_ONLY_ONE_VIDEO_CAPTURE)
            {
                NumberVideoCaptures = 1;
            }
            else
            {
                NumberVideoCaptures = 2;
            }
            CaptureOctwordsWritten = 4; /* This is the standard burst size for Video                                // capture in the Rage128. */
            CaptureDataRate = 35.0*1024*1024/1000000000;  /* 35 MBytes / sec. (It is */
                            /* only 27 for NTSC if you want to take advantage of this). */
            CaptureXCLKs = OverlayDescriptor->OpenPenalty + CaptureOctwordsWritten *
                           OverlayDescriptor->CyclesPerOctword +
                           OverlayDescriptor->WriteClosePenalty;

            DataCapturedInScalerHBlank = TotalScalerHBlankInVCLKs *
                                         VCLKPeriodInNSec * CaptureDataRate;
            NumCapAccessInScalerHBlank = (DWORD)ceil(DataCapturedInScalerHBlank /
                                      (CaptureOctwordsWritten*BytesPerOctWord));
            XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - CaptureXCLKs *
                                      NumCapAccessInScalerHBlank * NumberVideoCaptures;

            NumberOfScalerBlankAccesses = NumberOfScalerBlankAccesses +
                                    NumCapAccessInScalerHBlank * NumberVideoCaptures;
        }

        /*      Refresh */
        /* Refresh is lower priority than the scaler.
        XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank - 6;
        NumberOfScalerBlankAccesses = NumberOfScalerBlankAccesses + 1;
        */

        /* If all of the accesses by higher priority clients are spaced out, */
        /* then the scaler will pay a penalty for each one. */
        XCLKsLeftInScalerHBlank = XCLKsLeftInScalerHBlank -
                                  (OverlayDescriptor->OpenPenalty +
                                  OverlayDescriptor->ReadClosePenalty) *
                                  NumberOfScalerBlankAccesses;

    }

    /* ********************************************************* */
    /* ** Decide how many lines can be read in with the given bandwidth, */
    /* ** or allow the application to override the decision. */
    /* ********************************************************* */

    /* Under Construction */

    /* ********************************************************* */
    /* ** Determine Physical Vertical Scaling Ratios */
    /* ********************************************************* */
    {
        double SCREEN_Height;
        int InterlaceFactor;

        SCREEN_Height = (OverlayDescriptor->PHYSICAL_SrcBottom -
                        OverlayDescriptor->PHYSICAL_SrcTop) /
                        (1 << OverlayDescriptor->LOG_V_DECIMATION_FACT);
        V_scale_ratio = (double)(OverlayDescriptor->PHYSICAL_DestBottom -
                        OverlayDescriptor->PHYSICAL_DestTop + 1) /
                        SCREEN_Height;

        if (OverlayDescriptor->CRTCInterlaceEnable)
        {
            InterlaceFactor = 2;
        }
        else
        {
            InterlaceFactor = 1;
        }

        OverlayRegFields->val_OV0_V_INC = (WORD)((1.0 / (V_scale_ratio)) *
                                          (1<< 12) * InterlaceFactor + 0.5);
        CRT_V_INC = OverlayRegFields->val_OV0_V_INC/InterlaceFactor;
    }

    /* Calculate The Vertical Scale Ratio Required Of Scaler */
    if (OverlayDescriptor->CRTCInterlaceEnable)
    {
        V_scale_ratio = (double)(OverlayDescriptor->PHYSICAL_DestBottom - OverlayDescriptor->PHYSICAL_DestTop + 1) / (OverlayDescriptor->PHYSICAL_SrcBottom - OverlayDescriptor->PHYSICAL_SrcTop) / 2;
    }
    else
    {
        V_scale_ratio = (double)(OverlayDescriptor->PHYSICAL_DestBottom - OverlayDescriptor->PHYSICAL_DestTop + 1) / (OverlayDescriptor->PHYSICAL_SrcBottom - OverlayDescriptor->PHYSICAL_SrcTop);
    }

    {
        DWORD WorstP1VInc;
        DWORD WorstP23VInc;
        DWORD P23IsOn;
        DWORD MinDroppedP1Lines;
        DWORD MinDroppedP23Lines;

        /* Determine the worst case V_INC values which indicate the maximum number of */
        /* lines per output line. */
        WorstP1VInc = (int)ceil(1.0 / V_scale_ratio);

        switch (OverlayDescriptor->SURFACE_FORMAT)
        {
            case 3:
            case 4:
            case 6:
            case 11:
            case 12:    WorstP23VInc = 0;
                        P23IsOn = 0;
                        break;
            case 9:     WorstP23VInc = (int)ceil(4.0 / V_scale_ratio);
                        P23IsOn = 1;
                        break;
            case 10:
            case 13:
            case 14:    WorstP23VInc = (int)ceil(2.0 / V_scale_ratio);
                        P23IsOn = 1;
                        break;
            default:    break;
        }

        if (WorstP1VInc > OverlayDescriptor->P1_MAX_LN_IN_PER_LN_OUT)
        {
            MinDroppedP1Lines = ((int)WorstP1VInc - OverlayDescriptor->P1_MAX_LN_IN_PER_LN_OUT);
            WorstP1VInc = OverlayDescriptor->P1_MAX_LN_IN_PER_LN_OUT;
        }
        else
        {
            MinDroppedP1Lines = 0;
        }

        if (WorstP23VInc > OverlayDescriptor->P23_MAX_LN_IN_PER_LN_OUT)
        {
            MinDroppedP23Lines = ((int)WorstP23VInc - OverlayDescriptor->P23_MAX_LN_IN_PER_LN_OUT);
            WorstP23VInc = OverlayDescriptor->P23_MAX_LN_IN_PER_LN_OUT;
        }
        else
        {
            MinDroppedP23Lines = 0;
        }
    }

    /* Setting this parameter gives the memory controller more time to fetch a line of data. */
    /* It can be used in situations where we are really short of bandwidth. */
    DoNotUseMostRecentlyFetchedLine = 0;

    /*ToDo_Active: Need to program P?_MAX_LN_IN_PER_LN_OUT based on mode, image size, and memory bandwidth. */
    /* For now, we let the application decide. */
    OverlayRegFields->val_OV0_P1_MAX_LN_IN_PER_LN_OUT = OverlayDescriptor->P1_MAX_LN_IN_PER_LN_OUT % 4;
    OverlayRegFields->val_OV0_P23_MAX_LN_IN_PER_LN_OUT = OverlayDescriptor->P23_MAX_LN_IN_PER_LN_OUT % 4;

    /* ToDo_Active: Need to determine what LOG_V_DECIMATION_FACT will be for Y */
    /* and UV planes based on mode, image size, and memory bandwidth. */
    /* For now, we let the application dictate this via */
    /* OverlayDescriptor->LOG_V_DECIMATION_FACT. */

    OverlayRegFields->val_OV0_PROGMBL_LOAD_START = 0;
    OverlayRegFields->val_OV0_SMART_SWITCH = 1;
    OverlayRegFields->val_OV0_BURST_PER_PLANE = 0xff;

    /* ********************************************************* */
    /* ** Program the Pitch Registers */
    /* ********************************************************* */

    if (OverlayDescriptor->SurfaceIsTiled)
    {
        /* We need to determine what the memory architecture it and check that the surface */
        /* BaseAddress and Pitch meet the criteria for a tiled surface. */
        /* CYCACC_ASSERT((OverlayDescriptor->SurfacePitch[0] % TileWidthInBytes)==0, */
        /* "SurfacePitch[0] (in Bytes) must be a multiple of TileWidthInBytes") */
        /* CYCACC_ASSERT((OverlayDescriptor->SurfacePitch[1] % TileWidthInBytes)==0, */
        /* "SurfacePitch[1] (in Bytes) must be a multiple of TileWidthInBytes") */
        /* CYCACC_ASSERT((OverlayDescriptor->LOG_V_DECIMATION_FACT>=0) && */
        /* (OverlayDescriptor->LOG_V_DECIMATION_FACT<=3), "LOG_V_DECIMATION_FACT must be 0, */
        /*  1, 2, or 3") */
        /* We should actually program each pitch individually depending on whether it will      // be used for tiled surfaces or not.  For tiled surfaces, we program the pitch to */
        /* the value the hardware needs to add to get from the beginning of the */
        /* in a band to the beginning of the first line in the next band. */
        /* This value is: */
        /*    pitch*16 - (16 - 1<<OverlayDescriptor->LOG_V_DECIMATION_FACT)*4. */
        /* - (16 - (1<<OverlayDescriptor->LOG_V_DECIMATION_FACT))*4 is number of one line       // steps backward which takes us from the last line */
        /* displayed in the in the current band to the first line we displayed in the       // current band. */
        /* Pitch*16 is the step from the beginning of one band to the beginning of the next. */
        OverlayRegFields->val_OV0_PITCH0_SKIP_LINES = OverlayDescriptor->LOG_V_DECIMATION_FACT;
        OverlayRegFields->val_OV0_PITCH1_SKIP_LINES = OverlayDescriptor->LOG_V_DECIMATION_FACT;


        /* The second term is subtracted to takes us back to the beginning of the tile. The       // first term is added to take us to the beginning of the next tile. */
        OverlayRegFields->val_OV0_PITCH0_VALUE = (OverlayDescriptor->SurfacePitch[0]/MemWordsInBytes)*TileHeight - (TileHeight -
                  (1<<OverlayDescriptor->LOG_V_DECIMATION_FACT)) * TileWidthInMemWords;
        OverlayRegFields->val_OV0_PITCH1_VALUE = (OverlayDescriptor->SurfacePitch[1]/MemWordsInBytes)*TileHeight - (TileHeight -
                  (1<<OverlayDescriptor->LOG_V_DECIMATION_FACT)) * TileWidthInMemWords;
        /* We need the 4 LSB's of the pitch in tiles because these are the bits that control */
        /* checkerboarding. */
        OverlayRegFields->val_OV0_PITCH0_IN_TILES_LSBS = (OverlayDescriptor->SurfacePitch[0]/TileWidthInBytes) & 0xf;
        OverlayRegFields->val_OV0_PITCH1_IN_TILES_LSBS = (OverlayDescriptor->SurfacePitch[1]/TileWidthInBytes) & 0xf;

    }

    /* We should actually program each pitch individually depending on whether it */
    /* will be used for tiled surfaces or not. */
    OverlayRegFields->val_OV0_PITCH0_SKIP_LINES = 0;
    OverlayRegFields->val_OV0_PITCH1_SKIP_LINES = 0;
    OverlayRegFields->val_OV0_PITCH0_VALUE = OverlayDescriptor->SurfacePitch[0] /
                    MemWordsInBytes * (1<<OverlayDescriptor->LOG_V_DECIMATION_FACT);
    OverlayRegFields->val_OV0_PITCH1_VALUE = OverlayDescriptor->SurfacePitch[1] /
                    MemWordsInBytes * (1<<OverlayDescriptor->LOG_V_DECIMATION_FACT);
    OverlayRegFields->val_OV0_PITCH0_IN_TILES_LSBS = 0;
    OverlayRegFields->val_OV0_PITCH1_IN_TILES_LSBS = 0;

    /* ********************************************************* */
    /* ** Determine how to best configure the vertical filter engine and */
    /* ** Determine set up the scaler to achieve the desired physical scaling ratio. */
    /* ********************************************************* */

    /* Note: In Rage128 (the first version) the data is fetched and stored only in */
    /* octwords. In future chips this will change. */
    /* One change will involve a horizontal pre-downscaler that will scale octwords */
    /* down by a factor of two (to produce a QWord) or four (to produce a DWORD). */
    /* Another change will involve the use of compression which will cause a QWord */
    /* of data expanded into an Octword of data. When these changes happen, the */
    /* number of octwords to fetch will no longer be directly related to the */
    /* OV0_P?_X_END values. */

    {
        int P1GroupSize;
        int P23GroupSize;
        int P1StepSize;
        int P23StepSize;

        Calc_H_INC_STEP_BY(
            OverlayDescriptor->SURFACE_FORMAT,
            H_scale_ratio,
            DisallowFourTapVertFiltering,
            DisallowFourTapUVVertFiltering,
            OverlayDescriptor->AllowPreDownScale,
            &OverlayRegFields->val_OV0_P1_H_INC,
            &OverlayRegFields->val_OV0_P1_H_STEP_BY,
            &OverlayRegFields->val_OV0_P23_H_INC,
            &OverlayRegFields->val_OV0_P23_H_STEP_BY,
            &P1GroupSize,
            &P1StepSize,
            &P23StepSize,
            &OverlayRegFields->val_OV0_P1_PREDWNSC_RATIO,
            &OverlayRegFields->val_OV0_P23_PREDWNSC_RATIO);

        P23GroupSize = 2;       /* Current vaue for all modes */

        OverlayRegFields->val_OV0_HORZ_PICK_NEAREST = OverlayDescriptor->HORZ_PICK_NEAREST;

      {
        int ThereIsTwoTapVerticalFiltering;
        ThereIsTwoTapVerticalFiltering = (OverlayRegFields->val_OV0_P1_H_STEP_BY!=0) || (OverlayRegFields->val_OV0_P23_H_STEP_BY!=0);
        if (ThereIsTwoTapVerticalFiltering && DoNotUseMostRecentlyFetchedLine)
        {
            OverlayRegFields->val_OV0_VERT_PICK_NEAREST = 1;
        }
        else
        {
            OverlayRegFields->val_OV0_VERT_PICK_NEAREST = OverlayDescriptor->VERT_PICK_NEAREST;
        }
      }

        /* ToDo_Active: At the moment we are not using iOV0_VID_BUF?_START_PIX, but instead         // are using iOV0_P?_X_START and iOV0_P?_X_END. We should use "start pix" and       // "width" to derive the start and end. */

        OverlayRegFields->val_OV0_P1_X_START = (int)LeftPixel % (MemWordsInBytes/BytesPerPixel);
        OverlayRegFields->val_OV0_P1_X_END = (int)((OverlayRegFields->val_OV0_P1_X_START + SourceWidthInPixels - 1) / P1StepSize) * P1StepSize;

        switch (OverlayDescriptor->SURFACE_FORMAT)
        {
            case 9:
            case 10:
            case 13:
            case 14:    /* ToDo_Active: The driver must insure that the initial value is */
                        /* a multiple of a power of two when decimating */
                        OverlayRegFields->val_OV0_P2_X_START = (int)LeftUVPixel %
                                                (MemWordsInBytes/BytesPerUVPixel);
                        OverlayRegFields->val_OV0_P2_X_END = (int)((OverlayRegFields->val_OV0_P2_X_START +
                                  SourceUVWidthInPixels - 1) / P23StepSize) * P23StepSize;
                        break;
            case 11:
            case 12:    OverlayRegFields->val_OV0_P2_X_START = (int)LeftUVPixel % (MemWordsInBytes/(BytesPerPixel*2));
                        OverlayRegFields->val_OV0_P2_X_END = (int)((OverlayRegFields->val_OV0_P2_X_START + SourceUVWidthInPixels - 1) / P23StepSize) * P23StepSize;
                        break;
            case 3:
            case 4:     OverlayRegFields->val_OV0_P2_X_START = OverlayRegFields->val_OV0_P1_X_START;
                        /* This value is needed only to allow proper setting of */
                        /* OverlayRegFields->val_OV0_PRESHIFT_P23_TO */
                        /* OverlayRegFields->val_OV0_P2_X_END = 0; */
                        break;
            case 6:     OverlayRegFields->val_OV0_P2_X_START = (int)LeftPixel % (MemWordsInBytes/BytesPerPixel);
                        OverlayRegFields->val_OV0_P2_X_END = (int)((OverlayRegFields->val_OV0_P1_X_START + SourceWidthInPixels - 1) / P23StepSize) * P23StepSize;
                        break;
            default:    /* insert debug statement here. */
                        break;
        }

        OverlayRegFields->val_OV0_P3_X_START = OverlayRegFields->val_OV0_P2_X_START;
        OverlayRegFields->val_OV0_P3_X_END = OverlayRegFields->val_OV0_P2_X_END;

        /* 2.5 puts the kernal 50% of the way between the source pixel that is off screen */
        /* and the first on-screen source pixel. "(float)valOV0_P?_H_INC / (1<<0xc)" is */
        /* the distance (in source pixel coordinates) to the center of the first */
        /* destination pixel. Need to add additional pixels depending on how many pixels */
        /* are fetched at a time and how many pixels in a set are masked. */
        /* P23 values are always fetched in groups of two or four. If the start */
        /* pixel does not fall on the boundary, then we need to shift preshift for */
        /* some additional pixels */

        {
            double ExtraHalfPixel;
            double tempAdditionalShift;
            double tempP1HStartPoint;
            double tempP23HStartPoint;
            double tempP1Init;
            double tempP23Init;

            if (OverlayRegFields->val_OV0_HORZ_PICK_NEAREST)
            {
                ExtraHalfPixel = 0.5;
            }
            else
            {
                ExtraHalfPixel = 0.0;
            }
            tempAdditionalShift = OverlayRegFields->val_OV0_P1_X_START %
                                  P1GroupSize + ExtraHalfPixel;
            tempP1HStartPoint = tempAdditionalShift + 2.5 +
                     ((float)OverlayRegFields->val_OV0_P1_H_INC / (1<<0xd));
            tempP1Init = (double)((int)(tempP1HStartPoint * (1<<0x5) + 0.5))
                         / (1<<0x5);

            /* P23 values are always fetched in pairs. If the start pixel is odd, then we */
            /* need to shift an additional pixel */
            /* Note that if the pitch is a multiple of two, and if we store fields using */
            /* the traditional planer format where the V plane and the U plane share the */
            /* same pitch, then OverlayRegFields->val_OV0_P2_X_START % P23Group */
            /* OverlayRegFields->val_OV0_P3_X_START % P23GroupSize. Either way */
            /* it is a requirement that the U and V start on the same polarity byte */
            /* (even or odd). */
            tempAdditionalShift = OverlayRegFields->val_OV0_P2_X_START %
                                  P23GroupSize + ExtraHalfPixel;
            tempP23HStartPoint = tempAdditionalShift + 2.5 +
                              ((float)OverlayRegFields->val_OV0_P23_H_INC / (1<<0xd));
            tempP23Init = (double)((int)(tempP23HStartPoint * (1<<0x5) + 0.5)) /
                          (1 << 0x5);
            OverlayRegFields->val_OV0_P1_H_ACCUM_INIT = (int)((tempP1Init -
                                                        (int)tempP1Init) * (1<<0x5));
            OverlayRegFields->val_OV0_PRESHIFT_P1_TO = (int)tempP1Init;
            OverlayRegFields->val_OV0_P23_H_ACCUM_INIT = (int)((tempP23Init -
                                                         (int)tempP23Init) * (1<<0x5));
            OverlayRegFields->val_OV0_PRESHIFT_P23_TO = (int)tempP23Init;
        }

        /* ********************************************************* */
        /* ** Calculate values for initializing the vertical accumulators */
        /* ********************************************************* */

        {
            double ExtraHalfLine;
            double ExtraFullLine;
            double tempP1VStartPoint;
            double tempP23VStartPoint;

            if (OverlayRegFields->val_OV0_VERT_PICK_NEAREST)
            {
                ExtraHalfLine = 0.5;
            }
            else
            {
                ExtraHalfLine = 0.0;
            }

            if (OverlayRegFields->val_OV0_P1_H_STEP_BY==0)
            {
                ExtraFullLine = 1.0;
            }
            else
            {
                ExtraFullLine = 0.0;
            }

            tempP1VStartPoint = 1.5 + ExtraFullLine + ExtraHalfLine + ((float)CRT_V_INC / (1<<0xd));
            if (tempP1VStartPoint>2.5 + 2*ExtraFullLine)
            {
                tempP1VStartPoint = 2.5 + 2*ExtraFullLine;
                OverlayRegFields->val_OV0_P1_V_ACCUM_INIT = (int)(tempP1VStartPoint * (1<<0x5) + 0.5);
            }

            if (OverlayRegFields->val_OV0_VERT_PICK_NEAREST)
            {
                ExtraHalfLine = 0.5;
            }
            else
            {
                ExtraHalfLine = 0.0;
            }

            if (OverlayRegFields->val_OV0_P23_H_STEP_BY==0)
            {
                ExtraFullLine = 1.0;
            }
            else
            {
                ExtraFullLine = 0.0;
            }

            switch (OverlayDescriptor->SURFACE_FORMAT)
            {
                case 10:
                case 13:
                case 14:    tempP23VStartPoint = 1.5 + ExtraFullLine + ExtraHalfLine +
                                                ((float)CRT_V_INC / (1<<0xe));
                            break;
                case 9:     tempP23VStartPoint = 1.5 + ExtraFullLine + ExtraHalfLine +
                                                ((float)CRT_V_INC / (1<<0xf));
                            break;
                case 3:
                case 4:
                case 6:
                case 11:
                case 12:    tempP23VStartPoint = 0;
                            break;
                default:    /* insert debug statement here */
                            break;
            }

            if (tempP23VStartPoint>2.5 + 2*ExtraFullLine)
            {
                tempP23VStartPoint = 2.5 + 2*ExtraFullLine;
            }

            OverlayRegFields->val_OV0_P23_V_ACCUM_INIT = (int)(tempP23VStartPoint * (1<<0x5) + 0.5);
        }
    }

    /* ********************************************************* */
    /* ** Calculate whether to use programmable or hard coded */
    /* ** coefficients, and program the coefficients as needed */
    /* ********************************************************* */

    /* ToDo_Active: When in pick nearest mode, we need to program the filter tap zero */
    /* coefficients to 0, 32, 0, 0. Or use hard coded coefficients. */
    R128_FilterSetup (OverlayDescriptor, OverlayRegFields);

    /* ToDo_Active: Must add the smarts into the driver to decide what type of filtering it */
    /* would like to do. For now, we let the test application decide. */
    OverlayRegFields->val_OV0_HC_COEF_ON_HORZ_Y = OverlayDescriptor->HC_COEF_ON_HORZ_Y;
    OverlayRegFields->val_OV0_HC_COEF_ON_HORZ_UV = OverlayDescriptor->HC_COEF_ON_HORZ_UV;
    OverlayRegFields->val_OV0_HC_COEF_ON_VERT_Y = OverlayDescriptor->HC_COEF_ON_VERT_Y;
    OverlayRegFields->val_OV0_HC_COEF_ON_VERT_UV = OverlayDescriptor->HC_COEF_ON_VERT_UV;

    /* ********************************************************* */
    /* ** Setup the Deinterlacing Hardware */
    /* ********************************************************* */

    /* Dinterlacing / Backend Auto Flipping setup */
    OverlayRegFields->val_OV0_VID_PORT_SELECT = 2;  /* Place auto flip under software control */
    OverlayRegFields->val_OV0_SOFT_BUF_NUM = 0;
    OverlayRegFields->val_OV0_SOFT_BUF_ODD = 0;
    OverlayRegFields->val_OV0_SOFT_REPEAT_FIELD = 0;

    /* if we toggle this bit once, then the hardware will think that the */
    /* most recently received buffer and type is OV0_SOFT_BUF_NUM and OV0_SOFT_BUF_ODD. */
    OverlayRegFields->val_OV0_SOFT_EOF_TOGGLE = 0;

    OverlayRegFields->val_OV0_DEINT_PAT = OverlayDescriptor->DeinterlacePattern;
    OverlayRegFields->val_OV0_DEINT_PAT_LEN_M1 = OverlayDescriptor->DeinterlacePatternLenMinusOne;

    OverlayRegFields->val_OV0_SHIFT_EVEN_DOWN = ((OverlayDescriptor->ShiftFieldDown & 1)==1);
    OverlayRegFields->val_OV0_SHIFT_ODD_DOWN = ((OverlayDescriptor->ShiftFieldDown & 2)==2);
    OverlayRegFields->val_OV0_P1_FIRST_LINE_EVEN = OverlayDescriptor->FIRST_LINE_EVEN;

    /* ********************************************************* */
    /* ** Miscellaneous Setup and Checks */
    /* ********************************************************* */

    OverlayRegFields->val_OV0_SURFACE_FORMAT = OverlayDescriptor->SURFACE_FORMAT;
    OverlayRegFields->val_OVERLAY_X_START = OverlayDescriptor->PHYSICAL_DestLeft;
    OverlayRegFields->val_OVERLAY_X_END = OverlayDescriptor->PHYSICAL_DestRight;
    OverlayRegFields->val_OVERLAY_Y_START = OverlayDescriptor->PHYSICAL_DestTop;
    OverlayRegFields->val_OVERLAY_Y_END = OverlayDescriptor->PHYSICAL_DestBottom;

    OverlayRegFields->val_OV0_PIX_EXPAND = OverlayDescriptor->PIX_EXPAND;
    OverlayRegFields->val_OV0_Y2R_TEMP = OverlayDescriptor->Y2R_TEMP;
    OverlayRegFields->val_OV0_SIGNED_UV = OverlayDescriptor->SIGNED_UV1;
    OverlayRegFields->val_OV0_GAMMA_SEL = OverlayDescriptor->GAMMA_SEL;
    OverlayRegFields->val_OV0_COMCORE_SHIFT_UP_ONE = OverlayDescriptor->COMCORE_SHIFT_UP_ONE;
    OverlayRegFields->val_OV0_DOUBLE_BUFFER_REGS = OverlayDescriptor->DOUBLE_BUFFER_REGS;
    OverlayRegFields->val_OV0_IGNORE_REPEAT_FIELD = OverlayDescriptor->IGNORE_REPEAT_FIELD;

    OverlayRegFields->val_OV0_DIS_LIMIT = OverlayDescriptor->DIS_LIMIT;
    OverlayRegFields->val_OV0_INT_EMU = OverlayDescriptor->INT_EMU;
    OverlayRegFields->val_OV0_BRIGHTNESS = OverlayDescriptor->BRIGHTNESS;

    if ((OverlayRegFields->val_OV0_GAMMA_SEL != 0) &&
        (OverlayRegFields->val_OV0_BRIGHTNESS != 0))
    {
        OverlayRegFields->val_OV0_BRIGHTNESS = 0;
    }

    if ((OverlayRegFields->val_OV0_DIS_LIMIT == 1) &&
        (OverlayRegFields->val_OV0_BRIGHTNESS != 0))
    {
        OverlayRegFields->val_OV0_DIS_LIMIT = 0;
    }

    switch (OverlayDescriptor->SURFACE_FORMAT)
    {
        case 3:
        case 4:
        case 6:     OverlayRegFields->val_OV0_SATURATION_U = 0x10;
                    OverlayRegFields->val_OV0_SATURATION_V = 0x10;
                    break;
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:    OverlayRegFields->val_OV0_SATURATION_U = OverlayDescriptor->SATURATION_U;
                    OverlayRegFields->val_OV0_SATURATION_V = OverlayDescriptor->SATURATION_V;
                    break;
        default:    /* insert debug statement here. */
                    break;
        }

    OverlayRegFields->val_OV0_VIDEO_KEY_CLR = OverlayDescriptor->VIDEO_KEY_CLR;
    OverlayRegFields->val_OV0_VIDEO_KEY_MSK = OverlayDescriptor->VIDEO_KEY_MSK;
    OverlayRegFields->val_OV0_VIDEO_KEY_FN = OverlayDescriptor->VIDEO_KEY_FN;
    OverlayRegFields->val_OV0_SCALE_Y2R_DIS = OverlayDescriptor->SCALE_Y2R_DIS;
    OverlayRegFields->val_OV0_SWAP_UV = 0;
    OverlayRegFields->val_OV0_EXTSENSE = 1;
    OverlayRegFields->val_OV0_SUBPIC_ONLY=0;

    OverlayRegFields->val_OV0_GRAPHICS_KEY_CLR = OverlayDescriptor->GRAPHICS_KEY_CLR;
    OverlayRegFields->val_OV0_GRAPHICS_KEY_MSK = OverlayDescriptor->GRAPHICS_KEY_MSK;
    OverlayRegFields->val_OV0_GRAPHICS_KEY_FN = OverlayDescriptor->GRAPHICS_KEY_FN;
    OverlayRegFields->val_OV0_COMPARE_MIX = OverlayDescriptor->COMPARE_MIX;

}
