/******************************Module*Header*******************************\
* Module Name: bank.c
*
* Functions to control VGA banking
*
* Copyright (c) 1992-1995 Microsoft Corporation
\**************************************************************************/


#include "driver.h"                 // private driver defines

void BankErrorTrap(PDEVSURF, LONG, BANK_JUST);
void Bank2Window(PDEVSURF, LONG, BANK_JUST, ULONG);
void Bank2Window2RW(PDEVSURF, LONG, BANK_JUST, ULONG);
void Bank2Window1RW(PDEVSURF, LONG, BANK_JUST, ULONG);
void Bank1Window2RW(PDEVSURF, LONG, BANK_JUST);
void Bank1Window(PDEVSURF, LONG, BANK_JUST);

LPBYTE pPtrWork;
LPBYTE pPtrSave;

/******************************Public*Routine******************************\
* SetUpBanking
*
* Set up banking for the current mode
* pdsurf and ppdev are the pointers to the current surface and device
* Relevant fields in the surface are set up for banking
\**************************************************************************/

BOOL SetUpBanking(PDEVSURF pdsurf, PPDEV ppdev)
{
    INT i;
    LONG lTemp;
    LONG lScansPerBank, lScan, lTotalScans, lTotalBanks;
    LONG lScansBetweenBanks;
    ULONG ulOffset;
    PVIDEO_BANK_SELECT BankInfo;
    UINT ReturnedDataLength;
    VIDEO_BANK_SELECT TempBankInfo;
    PBANK_INFO pbiWorking;
    DWORD status;

    //
    // Query the miniport for banking info for this mode.
    //
    // First, figure out how big a buffer we need for the banking info
    // (returned in TempBankInfo->Size).
    //

    if (status = EngDeviceIoControl(ppdev->hDriver,
                                    IOCTL_VIDEO_GET_BANK_SELECT_CODE,
                                    NULL,
                                    0,
                                    &TempBankInfo,
                                    sizeof(VIDEO_BANK_SELECT),
                                    &ReturnedDataLength)) {

        //
        // We expect this call to fail, because we didn't allow any room
        // for the code; we just want to get the required output buffer
        // size. Make sure we got the expected error, ERROR_MORE_DATA.
        //

        if (status != ERROR_MORE_DATA) {
            //
            // Should post error and return FALSE
            //

            //RIP("Initialization error-GetBankSelectCode, first call\n");
            //return FALSE;
        }

    }


    //
    // Now, allocate a buffer of the required size and get the banking info.
    //

    if ((BankInfo = (PVIDEO_BANK_SELECT)
            EngAllocMem(0, TempBankInfo.Size, ALLOC_TAG)) == NULL) {

        //
        // Should post error and return FALSE
        //

        RIP("Initialization error-couldn't get memory for bank info\n");
        return FALSE;


    }

    if (EngDeviceIoControl(ppdev->hDriver,
                           IOCTL_VIDEO_GET_BANK_SELECT_CODE,
                           NULL,
                           0,
                           BankInfo,
                           TempBankInfo.Size,
                           &ReturnedDataLength)) {

        //
        // Should post error and return FALSE
        //

        RIP("Initialization error-GetBankSelectCode, second call\n");
        return FALSE;

    }

    //
    // Set up for banking.
    //

    // Set up variables that are the same whether the adapter is banked or not

    pdsurf->pBankSelectInfo = BankInfo;
    pdsurf->pfnBankSwitchCode =
         (PFN) (((UCHAR *)BankInfo) + BankInfo->CodeOffset);
    pdsurf->vbtBankingType = BankInfo->BankingType;
    pdsurf->ulBitmapSize = BankInfo->BitmapSize;
    pdsurf->lNextScan = BankInfo->BitmapWidthInBytes;

    // Set up the pointer off-screen work areas at the very end of display
    // memory (values are relative to bitmap start)
    ppdev->pPtrWork = pPtrWork = (LPBYTE) pdsurf->ulBitmapSize - POINTER_WORK_AREA_SIZE;
    ppdev->pPtrSave = pPtrSave = pPtrWork - POINTER_SAVE_AREA_SIZE;

    ppdev->sizlMem.cx = BankInfo->BitmapWidthInBytes *
                        (PLANAR_PELS_PER_CPU_ADDRESS);
    ppdev->sizlMem.cy = BankInfo->BitmapSize / BankInfo->BitmapWidthInBytes;

    DISPDBG((1,"ppdev->sizlMem.cx = %lu\n",ppdev->sizlMem.cx));
    DISPDBG((1,"ppdev->sizlMem.cy = %lu\n",ppdev->sizlMem.cy));
    DISPDBG((1,"BankInfo->Size = %lu\n",BankInfo->Size));
    DISPDBG((1,"BankInfo->Length = %lu\n",BankInfo->Length));

    //
    // calculate how many scans of offscreen memory are used by the pointer
    //

    ppdev->cNumScansUsedByPointer =
        (POINTER_SAVE_AREA_SIZE + POINTER_WORK_AREA_SIZE +
         BankInfo->BitmapWidthInBytes - 1) /
        (BankInfo->BitmapWidthInBytes);

    DISPDBG((1,"Software pointer uses %lu scans\n", ppdev->cNumScansUsedByPointer));

    //
    // for 1280x1024x16 in 1M, check to see that we actually have the
    // scans to put the pointer in offscreen video memory.  If not,
    // lie and set flag that keeps the driver from trying.
    //

    if ((LONG)ppdev->cNumScansUsedByPointer > (ppdev->sizlMem.cy - ppdev->sizlSurf.cy))
    {
        ppdev->fl &= ~DRIVER_OFFSCREEN_REFRESHED;
    }

    // Set up info that depends on whether or not the adapter is banked

    if (BankInfo->BankingType == VideoNotBanked) {

        lTotalScans = BankInfo->BitmapSize / BankInfo->BitmapWidthInBytes;

        // Unbanked; set all clip rects for the full bitmap, so the bank never
        // needs to be changed, and set the banking vectors to error traps,
        // since they should never be called

        pdsurf->rcl1WindowClip.left = pdsurf->rcl2WindowClip[0].left =
            pdsurf->rcl2WindowClip[1].left = pdsurf->rcl1WindowClip.top =
            pdsurf->rcl2WindowClip[0].top = pdsurf->rcl2WindowClip[1].top = 0;

        pdsurf->rcl1WindowClip.right = pdsurf->rcl2WindowClip[0].right =
            pdsurf->rcl2WindowClip[1].right =
                    BankInfo->BitmapWidthInBytes << 8;

        pdsurf->rcl1WindowClip.bottom = pdsurf->rcl2WindowClip[0].bottom =
            pdsurf->rcl2WindowClip[1].bottom = lTotalScans;

        pdsurf->pfnBankControl = BankErrorTrap;
        pdsurf->pfnBankControl2Window = BankErrorTrap;

        pdsurf->ulWindowBank[0] = pdsurf->ulWindowBank[1] = (ULONG)0;
        pdsurf->pvBitmapStart = pdsurf->pvBitmapStart2Window[0] =
            pdsurf->pvBitmapStart2Window[1] = pdsurf->pvStart;

        // Scan line to be used with JustifyBottom to map in the bank
        // containing the pointer work and save areas, which are guaranteed not
        // to span banks. Is only used to determine that the bank never needs
        // to be changed to map in the pointer in unbanked case
        pdsurf->ulPtrBankScan = 0;

        // Mark that the bank info pointers are unused, so we don't try to
        // deallocate the memory they point to
        pdsurf->pbiBankInfo = pdsurf->pbiBankInfo2RW = NULL;

        // Allocate space for the temp buffer.

        if ((pdsurf->pvBankBufferPlane0 =
                (PVOID) EngAllocMem(0, BANK_BUFFER_SIZE_UNBANKED, ALLOC_TAG))
            == NULL) {

            //
            // Should post error and return FALSE
            //

            RIP("Couldn't get memory for temp buffer");
            return FALSE;

        }

        pdsurf->ulTempBufferSize = BANK_BUFFER_SIZE_UNBANKED;

        // These should never be used
        pdsurf->pvBankBufferPlane1 =
            pdsurf->pvBankBufferPlane2 =
            pdsurf->pvBankBufferPlane3 = (PVOID) NULL;

    } else {

        // Banked, so set up all banking variables and initialize the bank
        // control routines and their data tables

        // Reject if there are broken rasters (a broken raster is a scan line
        // that crosses a bank boundary); that's not handled in this driver

        if (BankInfo->BankingType != VideoBanked2RW) {

            // For the 1 RW window and 1R1W window cases, windows are
            // assumed to be BANK_SIZE_1_WINDOW in size (generally 64K)
            lTemp = BANK_SIZE_1_WINDOW;

        } else {

            // For the 2 RW window case, windows are assumed to be
            // BANK_SIZE_2RW_WINDOW in size (generally 32K)
            lTemp = BANK_SIZE_2RW_WINDOW;

        }

        if ((lTemp % BankInfo->BitmapWidthInBytes) != 0) {

            //
            // Should post error and return FALSE
            //

            RIP("Broken rasters not supported");
            return FALSE;

        }

        // These will be set properly on first call to bank controller, below,
        // or something's wrong
        pdsurf->ulWindowBank[0] = (ULONG)-1;
        pdsurf->ulWindowBank[1] = (ULONG)-1;
        pdsurf->pvBitmapStart = pdsurf->pvBitmapStart2Window[0] =
            pdsurf->pvBitmapStart2Window[1] = (PVOID) 0;

        // Set all clip rects to invalid; they'll be updated when the first
        // bank is mapped in

        pdsurf->rcl1WindowClip.left = pdsurf->rcl2WindowClip[0].left =
            pdsurf->rcl2WindowClip[1].left = pdsurf->rcl1WindowClip.top =
            pdsurf->rcl2WindowClip[0].top = pdsurf->rcl2WindowClip[1].top =
            pdsurf->rcl1WindowClip.right = pdsurf->rcl2WindowClip[0].right =
            pdsurf->rcl2WindowClip[1].right = pdsurf->rcl1WindowClip.bottom =
            pdsurf->rcl2WindowClip[0].bottom =
            pdsurf->rcl2WindowClip[1].bottom = -1;

        // Set up to call the appropriate banking control routines

        switch(BankInfo->BankingType) {

            case VideoBanked1RW:

                pdsurf->pfnBankControl = Bank1Window;
                pdsurf->pfnBankControl2Window = Bank2Window1RW;

                if ((pdsurf->pvBankBufferPlane0 =
                        (PVOID) EngAllocMem(0, BANK_BUFFER_SIZE_1RW, ALLOC_TAG))
                    == NULL) {

                    //
                    // Should post error and return FALSE
                    //

                    RIP("Couldn't get memory for temp buffer");
                    return FALSE;

                }

                pdsurf->ulTempBufferSize = BANK_BUFFER_SIZE_1RW;

                pdsurf->pvBankBufferPlane1 =
                        ((LPBYTE)pdsurf->pvBankBufferPlane0) +
                        BANK_BUFFER_PLANE_SIZE;
                pdsurf->pvBankBufferPlane2 =
                        ((LPBYTE)pdsurf->pvBankBufferPlane1) +
                        BANK_BUFFER_PLANE_SIZE;
                pdsurf->pvBankBufferPlane3 =
                        ((LPBYTE)pdsurf->pvBankBufferPlane2) +
                        BANK_BUFFER_PLANE_SIZE;

                break;


            case VideoBanked1R1W:

                pdsurf->pfnBankControl = Bank1Window;
                pdsurf->pfnBankControl2Window = Bank2Window;

                if ((pdsurf->pvBankBufferPlane0 =
                        (PVOID) EngAllocMem(0, BANK_BUFFER_SIZE_1R1W, ALLOC_TAG))
                    == NULL) {

                    //
                    // Should post error and return FALSE
                    //

                    RIP("Couldn't get memory for temp buffer");
                    return FALSE;

                }

                pdsurf->ulTempBufferSize = BANK_BUFFER_SIZE_1R1W;

                // These should never be used
                pdsurf->pvBankBufferPlane1 =
                        pdsurf->pvBankBufferPlane2 =
                        pdsurf->pvBankBufferPlane3 = (PVOID) NULL;

                break;


            case VideoBanked2RW:

                pdsurf->pfnBankControl = Bank1Window2RW;
                pdsurf->pfnBankControl2Window = Bank2Window2RW;

                if ((pdsurf->pvBankBufferPlane0 =
                        (PVOID) EngAllocMem(0, BANK_BUFFER_SIZE_2RW, ALLOC_TAG))
                     == NULL) {

                    //
                    // Should post error and return FALSE
                    //

                    RIP("Couldn't get memory for temp buffer");
                    return FALSE;

                }

                pdsurf->ulTempBufferSize = BANK_BUFFER_SIZE_2RW;

                // These should never be used
                pdsurf->pvBankBufferPlane1 =
                    pdsurf->pvBankBufferPlane2 =
                    pdsurf->pvBankBufferPlane3 = (PVOID) NULL;

                break;

            default:

                //
                // Should post error and return FALSE
                //

                RIP("bad BankingType");
                return FALSE;

        }

        // Set up the bank control tables with clip rects for banks
        // Note: lTotalBanks is generally an overestimate when granularity
        // is less than window size, because we ignore any banks after the
        // first one that includes the last scan line of the bitmap. A bit
        // of memory could be saved by sizing lTotalBanks exactly. Note too,
        // though, that the 2 RW window case may require more entries then,
        // because its windows are shorter, so you'd have to make sure there
        // were enough entries for the 2 RW window case, or recalculate
        // lTotalBanks for the 2 RW case

        lTotalBanks = BankInfo->BitmapSize / BankInfo->Granularity;
        lTotalScans = BankInfo->BitmapSize / BankInfo->BitmapWidthInBytes;
        lScansBetweenBanks =
                BankInfo->Granularity / BankInfo->BitmapWidthInBytes;

        // Allocate memory for bank control info

        if ((pdsurf->pbiBankInfo = (PBANK_INFO)
                EngAllocMem(0, lTotalBanks * sizeof(BANK_INFO), ALLOC_TAG))
             == NULL) {

            //
            // Should post error and return FALSE
            //

            RIP("Couldn't get memory for bank info #2");
            return FALSE;

        }

        // Build the list of bank rects & offsets assuming standard window
        // size

        lScansPerBank = BANK_SIZE_1_WINDOW / BankInfo->BitmapWidthInBytes;
        ulOffset = 0;
        lScan = -lScansBetweenBanks;    // precompensate for 1st time in loop
        i = 0;
        pbiWorking = pdsurf->pbiBankInfo;

        do {

            lScan += lScansBetweenBanks;
            pbiWorking->rclBankBounds.left = 0;
            pbiWorking->rclBankBounds.right = pdsurf->sizlSurf.cx;
            pbiWorking->rclBankBounds.top = lScan;
            pbiWorking->rclBankBounds.bottom = lScan + lScansPerBank;
            pbiWorking->ulBankOffset = ulOffset;
            ulOffset += BankInfo->Granularity;
            i++;
            pbiWorking++;

        } while ((lScan + lScansPerBank) < lTotalScans);

        pdsurf->ulBankInfoLength = i;

        // If this is a 2RW bank adapter, build a table for that too, with
        // 32K windows

        if (BankInfo->BankingType == VideoBanked2RW) {

            // Offset from one bank index to next to make two 32K banks
            // appear to be one seamless 64K bank
            pdsurf->ulBank2RWSkip =
                    (BANK_SIZE_2RW_WINDOW / BankInfo->Granularity);

            // Allocate memory for 2RW case bank control info
            if ((pdsurf->pbiBankInfo2RW =
                    (PBANK_INFO) EngAllocMem(0, lTotalBanks * sizeof(BANK_INFO), ALLOC_TAG))
                == NULL) {

                //
                // Should post error and return FALSE
                //

                RIP("Couldn't get memory for bank info #3");
                return FALSE;

            }

            // Build the list of bank rects & offsets for 2RW case
            lScansPerBank =
                    BANK_SIZE_2RW_WINDOW / BankInfo->BitmapWidthInBytes;
            lScan = -lScansBetweenBanks;    // precompensate for 1st time
            ulOffset = 0;
            i = 0;
            pbiWorking = pdsurf->pbiBankInfo2RW;

            do {

                lScan += lScansBetweenBanks;
                pbiWorking->rclBankBounds.left = 0;
                pbiWorking->rclBankBounds.right =  pdsurf->sizlSurf.cx;
                pbiWorking->rclBankBounds.top = lScan;
                pbiWorking->rclBankBounds.bottom = lScan + lScansPerBank;
                pbiWorking->ulBankOffset = ulOffset;
                ulOffset += BankInfo->Granularity;
                i++;
                pbiWorking++;

            } while ((lScan + lScansPerBank) < lTotalScans);

            pdsurf->ulBankInfo2RWLength = i;

        } else {

            // Not a 2RW bank adapter

            pdsurf->pbiBankInfo2RW = NULL;
            pdsurf->ulBankInfo2RWLength = 0;

        }

        // Map in scan line 0 for read & write, to put things in a known state

        pdsurf->pfnBankControl(pdsurf, 0, JustifyTop);

        // Scan line to be used with JustifyBottom to map in the bank
        // containing the pointer work and save areas, which are guaranteed not
        // to span banks
        pdsurf->ulPtrBankScan = (pdsurf->ulBitmapSize/pdsurf->lNextScan) - 1;

    }


    return TRUE;
}

/******************************Private*Routine******************************\
* BankErrorTrap
*
* Traps calls to bank control functions in non-banked modes
*
\**************************************************************************/

void BankErrorTrap(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification)
{
    DISPDBG((0, "Call to bank manager in unbanked mode\n"));
}


/******************************Private*Routine******************************\
* Bank1Window
*
* Maps in a single R/W window that allows access to lScan. Applies to both
* 1 RW window and 1R1W window banking schemes.
*
* Note: in the 1 R/W adapter case, this may be called with a fourth parameter
* (the source/dest selector), which is ignored. This is so that we can use the
* same routine as the destination for 1 R/W 2-window calls; those calls don't
* map in separate banks, of course, but they let us get away with common code
* for 1 R/W and 1R/1W in some cases (such as aligned blits).
*
\**************************************************************************/

void Bank1Window(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification)
{
    ULONG ulBank;
    PBANK_INFO pbiWorking;
    volatile ULONG ulBank0;
    volatile PFN pBankFn;

    // ASM routines that call this may have STD in effect, but the C compiler
    // assumes CLD

    _asm    pushfd
    _asm    cld

    // Find the bank containing the scan line with the desired justification
    if (ulJustification == JustifyTop) {

        // Map scan line in as near as possible to the top of the window
        ulBank = pdsurf->ulBankInfoLength-1;
        pbiWorking = pdsurf->pbiBankInfo + ulBank;
        while (pbiWorking->rclBankBounds.top > lScan) {
            ulBank--;
            pbiWorking--;
        }

    } else {

        // Map scan line in as near as possible to the bottom of the window
        ulBank = 0;
        pbiWorking = pdsurf->pbiBankInfo;
        while (pbiWorking->rclBankBounds.bottom <= lScan) {
            ulBank++;
            pbiWorking++;
        }

    }

    // Set the clip rect for this bank; if it's set to -1, that indicates that
    // a double-window set-up is currently active, so invalidate double-window
    // clip rects and display memory pointers (when double-window is active,
    // single-window is inactive, and vice-versa; a full bank set-up has to be
    // performed to switch between the two)

    if (pdsurf->rcl1WindowClip.top == -1) {

        pdsurf->rcl2WindowClip[0].top =
        pdsurf->rcl2WindowClip[0].bottom =
        pdsurf->rcl2WindowClip[0].right =
        pdsurf->rcl2WindowClip[0].left =
        pdsurf->rcl2WindowClip[1].top =
        pdsurf->rcl2WindowClip[1].bottom =
        pdsurf->rcl2WindowClip[1].right =
        pdsurf->rcl2WindowClip[1].left = -1;
        pdsurf->pvBitmapStart2Window[0] = (PDEVSURF) 0;
        pdsurf->pvBitmapStart2Window[1] = (PDEVSURF) 0;

    } else {
//        ASSERT(pdsurf->rcl2WindowClip[0].top == -1,
//                "BANK.C: 2 bank src not mapped out");
//        ASSERT(pdsurf->rcl2WindowClip[1].top == -1,
//                "BANK.C: 2 bank src not mapped out");
    }


    pdsurf->rcl1WindowClip = pbiWorking->rclBankBounds;

    // Shift the bitmap start address so that the desired bank aligns with
    // the banking window. The source and destination are set only so 1 R/W
    // aligned blits will work without having to be specifically aware of
    // the adapter type (some of the same code works with 1R/1W adapters too).

    pdsurf->pvBitmapStart =
    pdsurf->pvBitmapStart2Window[0] =
    pdsurf->pvBitmapStart2Window[1] =
            (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);

    // Map in the desired bank for both read and write
    // This is so convoluted to avoid problems with wiping out registers C
    // thinks it's still using; the values are tranferred to volatiles, and
    // then to registers

    ulBank0 = ulBank;
    pBankFn = pdsurf->pfnBankSwitchCode;
    _asm mov eax,ulBank0;
    _asm mov edx,eax;
    _asm call pBankFn;    // actually switch the banks

    _asm    popfd
}


/******************************Private*Routine******************************\
* Bank1Window2RW
*
* Maps in two 32K RW windows so that they form a single 64K R/W window that
* allows access to lScan. Applies only to 2 RW window schemes.
*
\**************************************************************************/

void Bank1Window2RW(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification)
{
    ULONG ulBank;
    PBANK_INFO pbiWorking;
    volatile ULONG ulBank0;
    volatile ULONG ulBank1;
    volatile PFN pBankFn;

    // ASM routines that call this may have STD in effect, but the C compiler
    // assumes CLD

    _asm    pushfd
    _asm    cld

    // Find the bank containing the scan line with the desired justification
    if (ulJustification == JustifyTop) {

        // Map scan line in as near as possible to the top of the window
        ulBank = pdsurf->ulBankInfoLength-1;
        pbiWorking = pdsurf->pbiBankInfo + ulBank;
        while (pbiWorking->rclBankBounds.top > lScan) {
            ulBank--;
            pbiWorking--;
        }

    } else {

        // Map scan line in as near as possible to the bottom of the window
        ulBank = 0;
        pbiWorking = pdsurf->pbiBankInfo;
        while (pbiWorking->rclBankBounds.bottom <= lScan) {
            ulBank++;
            pbiWorking++;
        }

    }

    // Set the clip rect for this bank; if it's set to -1, that indicates that
    // a double-window set-up is currently active, so invalidate double-window
    // clip rects and display memory pointers (when double-window is active,
    // single-window is inactive, and vice-versa; a full bank set-up has to be
    // performed to switch between the two)

    if (pdsurf->rcl1WindowClip.top == -1) {

        pdsurf->rcl2WindowClip[0].top =
        pdsurf->rcl2WindowClip[0].bottom =
        pdsurf->rcl2WindowClip[0].right =
        pdsurf->rcl2WindowClip[0].left =
        pdsurf->rcl2WindowClip[1].top =
        pdsurf->rcl2WindowClip[1].bottom =
        pdsurf->rcl2WindowClip[1].right =
        pdsurf->rcl2WindowClip[1].left = -1;
        pdsurf->pvBitmapStart2Window[0] = (PDEVSURF) 0;
        pdsurf->pvBitmapStart2Window[1] = (PDEVSURF) 0;

    } else {
//        ASSERT(pdsurf->rcl2WindowClip[0].top == -1,
//                "BANK.C: 2 bank src not mapped out");
//        ASSERT(pdsurf->rcl2WindowClip[1].top == -1,
//                "BANK.C: 2 bank src not mapped out");
    }


    pdsurf->rcl1WindowClip = pbiWorking->rclBankBounds;

    // Shift the bitmap start address so that the desired bank aligns with
    // the banking window. The source and destination are set only so 1 R/W
    // aligned blits will work without having to be specifically aware of
    // the adapter type (some of the same code works with 1R/1W adapters too).

    pdsurf->pvBitmapStart =
    pdsurf->pvBitmapStart2Window[0] =
    pdsurf->pvBitmapStart2Window[1] =
            (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);

    // Map in the desired bank for both read and write; this is accomplished
    // by mapping in the desired 32K bank, followed by the next 32K bank.
    // This is so convoluted to avoid problems with wiping out registers C
    // thinks it's still using; the values are tranferred to volatiles, and
    // then to registers

    ulBank0 = ulBank;
    ulBank1 = ulBank0 + pdsurf->ulBank2RWSkip;
    pBankFn = pdsurf->pfnBankSwitchCode;
    _asm mov eax,ulBank0;
    _asm mov edx,ulBank1;
    _asm call pBankFn;    // actually switch the banks

    _asm    popfd
}


/******************************Private*Routine******************************\
* Bank2Window
*
* Maps in one of two windows, either the source window (window 0) or the dest
* window (window 1), to allows access to lScan. Applies to 1R1W window
* banking scheme; should never be called for 1 RW window schemes, because
* there's only one window in that case.
*
\**************************************************************************/

void Bank2Window(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification, ULONG ulWindowToMap)
{
    ULONG ulBank;
    PBANK_INFO pbiWorking;
    volatile ULONG ulBank0, ulBank1;
    volatile PFN pBankFn;

    // ASM routines that call this may have STD in effect, but the C compiler
    // assumes CLD

    _asm    pushfd
    _asm    cld

    // Find the bank containing the scan line with the desired justification
    if (ulJustification == JustifyTop) {

        // Map scan line in as near as possible to the top of the window
        ulBank = pdsurf->ulBankInfoLength-1;
        pbiWorking = pdsurf->pbiBankInfo + ulBank;
        while (pbiWorking->rclBankBounds.top > lScan) {
            ulBank--;
            pbiWorking--;
        }

    } else {

        // Map scan line in as near as possible to the bottom of the window
        ulBank = 0;
        pbiWorking = pdsurf->pbiBankInfo;
        while (pbiWorking->rclBankBounds.bottom <= lScan) {
            ulBank++;
            pbiWorking++;
        }

    }

    // Set the clip rect for this bank; if it's set to -1, that indicates that
    // a single-window set-up is currently active, so invalidate single-window
    // clip rects and display memory pointers (when double-window is active,
    // single-window is inactive, and vice-versa; a full bank set-up has to be
    // performed to switch between the two)

    if (pdsurf->rcl2WindowClip[ulWindowToMap].top == -1) {

        pdsurf->rcl1WindowClip.top =
        pdsurf->rcl1WindowClip.bottom =
        pdsurf->rcl1WindowClip.right =
        pdsurf->rcl1WindowClip.left = -1;
        pdsurf->pvBitmapStart = (PDEVSURF) 0;

        // Neither of the 2 window windows was active, so we have to set up the
        // variables for the other bank (the one other than the one we were
        // called to set) as well, to make it valid. The other bank is set to
        // the same state as the bank we were called to set
        pdsurf->rcl2WindowClip[ulWindowToMap^1] = pbiWorking->rclBankBounds;
        pdsurf->pvBitmapStart2Window[ulWindowToMap^1] =
                (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);
        pdsurf->ulWindowBank[ulWindowToMap^1] = ulBank;
    } else {
//        ASSERT(pdsurf->rcl1WindowClip.top == -1,
//                "BANK.C: 1 bank not mapped out");
    }

    pdsurf->rcl2WindowClip[ulWindowToMap] = pbiWorking->rclBankBounds;

    // Shift the bitmap start address so that the desired bank aligns with the
    // banking window

    pdsurf->pvBitmapStart2Window[ulWindowToMap] =
            (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);

    // Map in the desired bank; also map in the other bank to whatever its
    // current state is

    pdsurf->ulWindowBank[ulWindowToMap] = ulBank;

    // Set both banks at once, because we may have just initialized the other
    // bank, and because this way the bank switch code doesn't have to do a
    // read before write to obtain the state of the other bank.
    // This is so convoluted to avoid problems with wiping out registers C
    // thinks it's still using; the values are tranferred to volatiles, and
    // then to registers

    ulBank0 = pdsurf->ulWindowBank[0];
    ulBank1 = pdsurf->ulWindowBank[1];
    pBankFn = pdsurf->pfnBankSwitchCode;
    _asm mov eax,ulBank0;
    _asm mov edx,ulBank1;
    _asm call pBankFn;    // actually switch the banks

    _asm    popfd
}


/******************************Private*Routine******************************\
* Bank2Window1RW
*
* Maps in the one window in 1R/W case.  Does exactly the same thing as the
* one window case, because there's only one window, but has to be a separate
* entry point because of the extra parameter (because we're using STDCALL).
\**************************************************************************/

void Bank2Window1RW(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification, ULONG ulWindowToMap)
{
    Bank1Window(pdsurf, lScan, ulJustification);
}


/******************************Private*Routine******************************\
* Bank2Window2RW
*
* Maps in one of two windows, either the source window (window 0) or the dest
* window (window 1), to allows access to lScan. Applies to 2RW window
* banking scheme; should never be called for 1 RW window schemes, because
* there's only one window in that case.
\**************************************************************************/

void Bank2Window2RW(PDEVSURF pdsurf, LONG lScan,
    BANK_JUST ulJustification, ULONG ulWindowToMap)
{
    ULONG ulBank;
    PBANK_INFO pbiWorking;
    volatile ULONG ulBank0, ulBank1;
    volatile PFN pBankFn;

    // ASM routines that call this may have STD in effect, but the C compiler
    // assumes CLD

    _asm    pushfd
    _asm    cld

    // Find the bank containing the scan line with the desired justification
    if (ulJustification == JustifyTop) {

        // Map scan line in as near as possible to the top of the window
        ulBank = pdsurf->ulBankInfo2RWLength-1;
        pbiWorking = pdsurf->pbiBankInfo2RW + ulBank;
        while (pbiWorking->rclBankBounds.top > lScan) {
            ulBank--;
            pbiWorking--;
        }

    } else {

        // Map scan line in as near as possible to the bottom of the window
        ulBank = 0;
        pbiWorking = pdsurf->pbiBankInfo2RW;
        while (pbiWorking->rclBankBounds.bottom <= lScan) {
            ulBank++;
            pbiWorking++;
        }

    }

    // Set the clip rect for this bank; if it's set to -1, that indicates that
    // a single-window set-up is currently active, so invalidate single-window
    // clip rects and display memory pointers (when double-window is active,
    // single-window is inactive, and vice-versa; a full bank set-up has to be
    // performed to switch between the two)

    if (pdsurf->rcl2WindowClip[ulWindowToMap].top == -1) {

        pdsurf->rcl1WindowClip.top =
        pdsurf->rcl1WindowClip.bottom =
        pdsurf->rcl1WindowClip.right =
        pdsurf->rcl1WindowClip.left = -1;
        pdsurf->pvBitmapStart = (PDEVSURF) 0;

        // Neither of the 2 window windows was active, so we have to set up the
        // variables for the other bank (the one other than the one we were
        // called to set) as well, to make it valid. The other bank is set to
        // the same state as the bank we were called to set
        pdsurf->rcl2WindowClip[ulWindowToMap^1] = pbiWorking->rclBankBounds;
        if (ulWindowToMap == 1) {
            pdsurf->pvBitmapStart2Window[0] =
                (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);
        } else {
            pdsurf->pvBitmapStart2Window[1] =
                (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset +
                BANK_SIZE_2RW_WINDOW);
        }
        pdsurf->ulWindowBank[ulWindowToMap^1] = ulBank;
    } else {
//        ASSERT(pdsurf->rcl1WindowClip.top == -1,
//                "BANK.C: 1 bank not mapped out");
    }

    pdsurf->rcl2WindowClip[ulWindowToMap] = pbiWorking->rclBankBounds;

    // Shift the bitmap start address so that the desired bank aligns with the
    // banking window

    if (ulWindowToMap == 0) {
        pdsurf->pvBitmapStart2Window[0] =
            (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset);
    } else {
        pdsurf->pvBitmapStart2Window[1] =
            (PVOID) ((UCHAR *)pdsurf->pvStart - pbiWorking->ulBankOffset +
            BANK_SIZE_2RW_WINDOW);
    }

    // Map in the desired bank; also map in the other bank to whatever its
    // current state is

    pdsurf->ulWindowBank[ulWindowToMap] = ulBank;

    // Set both banks at once, because we may have just initialized the other
    // bank, and because this way the bank switch code doesn't have to do a
    // read before write to obtain the state of the other bank.
    // This is so convoluted to avoid problems with wiping out registers C
    // thinks it's still using; the values are tranferred to volatiles, and
    // then to registers

    ulBank0 = pdsurf->ulWindowBank[0];
    ulBank1 = pdsurf->ulWindowBank[1];
    pBankFn = pdsurf->pfnBankSwitchCode;
    _asm mov eax,ulBank0;
    _asm mov edx,ulBank1;
    _asm call pBankFn;    // actually switch the banks

    _asm    popfd
}


/************************************************************************\
* DrvIntersectRect
*
* Calculates the intersection between *prcSrc1 and *prcSrc2,
* returning the resulting rect in *prcDst.  Returns TRUE if
* *prcSrc1 intersects *prcSrc2, FALSE otherwise.  If there is no
* intersection, an empty rect is returned in *prcDst
\************************************************************************/
static const RECTL rclEmpty = { 0, 0, 0, 0 };

BOOL DrvIntersectRect(
    PRECTL prcDst,
    PRECTL prcSrc1,
    PRECTL prcSrc2)

{
    prcDst->left  = max(prcSrc1->left, prcSrc2->left);
    prcDst->right = min(prcSrc1->right, prcSrc2->right);

    /*
     * check for empty rect
     */
    if (prcDst->left < prcDst->right) {

        prcDst->top = max(prcSrc1->top, prcSrc2->top);
        prcDst->bottom = min(prcSrc1->bottom, prcSrc2->bottom);

        /*
         * check for empty rect
         */
        if (prcDst->top < prcDst->bottom) {
            return TRUE;        // not empty
        }
    }

    /*
     * empty rect
     */
    *prcDst = rclEmpty;

    return FALSE;
}

/************************************************************************\
* vForceBank0
*
* Forces the VGA to map in bank 0 if there's banking. Intended for use
* when returning from fullscreen, so a known bank is mapped.
\************************************************************************/
VOID vForceBank0(
PPDEV ppdev)
{
    PDEVSURF pdsurf = ppdev->pdsurf;

    if (pdsurf->vbtBankingType != VideoNotBanked) {

        // Set all clip rects to invalid; they'll be updated when we map in
        // bank 0
        pdsurf->rcl1WindowClip.left = pdsurf->rcl2WindowClip[0].left =
            pdsurf->rcl2WindowClip[1].left = pdsurf->rcl1WindowClip.top =
            pdsurf->rcl2WindowClip[0].top = pdsurf->rcl2WindowClip[1].top =
            pdsurf->rcl1WindowClip.right = pdsurf->rcl2WindowClip[0].right =
            pdsurf->rcl2WindowClip[1].right = pdsurf->rcl1WindowClip.bottom =
            pdsurf->rcl2WindowClip[0].bottom =
            pdsurf->rcl2WindowClip[1].bottom = -1;

        // Map in scan line 0 for read & write, to put things in a known state
        pdsurf->pfnBankControl(pdsurf, 0, JustifyTop);
    }
}