;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE    ,132
        NAME    VVCOPY
        TITLE   Virtual Video Device Driver Copy Support

;/*****************************************************************************
;*
;* SOURCE FILE NAME = VVCOPY.ASM
;*
;* DESCRIPTIVE NAME = Virtual Video Device Driver Copy Support.
;*
;*
;* VERSION      V2.0
;*
;* DATE         08/31/89
;*
;* DESCRIPTION  This module contains the LVB and graphics bitmap copy functions.
;*
;* FUNCTIONS
;*              vvCopyLVBToLVB()              Copy rectangle of LVB to specfied LVB
;*              vvCopyBlankToLVB()            Copy blank rectangle to specified LVB
;*              vvCopyGraphicsToBitmap256()   Copy graphics rectangle in bitmap format
;*              vvCopyGraphicsToBitmap()      Copy graphics rectangle in bitmap format
;*              vvCopyGraphicsToDDBitmap()    Copy graphics rectangle in DD-bitmap format
;*              vvAnalyzeColors()             Determine if plane order can be improved
;*              vvConvertPalette()            Copy VDM palette to RGB format
;*              vvSetupShadow()               Setup shadow buffer dimensions
;*              vvFillShadowBuffer()          Fill shadow buffer with data
;*              vvDiffShadowBuffer()          Find differences in buffers
;*              vvDiffPhysPage()              Diff-and-copy page of physical memory (all planes)
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        CHANGE DESCRIPTION
;*   --------  ----------  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxxxx
;*   08/31/89              Created.
;*   07/08/91              B724503 - Workaround a B1 Stepping level     of
;*                                   the 80386 where a REP MOVS followed by
;*                                   a 16-bit POP can cause the high word of
;*                                   EDI or ESI to not get updated properly
;*                                   when the operation overflows 64K.
;*   02/04/92              B731109 - Use LVB size for validation.
;*   02/15/92              B733006 - Performance enhancement to copy routines.
;*   02/26/92              B733566 - Back out the fix for J200435.
;*   02/27/92              B733211 - Performance enhancement to 320x200 mode.
;*   05/15/92              SM11856 - changes for 32-bit DISPLAY driver.
;*   07/12/92              24769   - Add 1024x768x16 bitmap copy support.
;*   10/15/92              Unknown - Background virtualisation required
;*                                   rewrite vvCopyGraphicsToBitmap256().
;*   07/12/92              56398   - Handle logical scanline lengths in
;*   11/19/92              57000   - Logical scanline lenght fix.
;*   11/25/92              57246   - check for overrun filling shadow buffer
;*   06/04/93              F69306  - Add support for S3
;*   11/01/93              75458     S3 Merge
;*
;*****************************************************************************/


        .xlist
        include mvdm.inc
        include vddseg.inc
        .list
        include vvd.inc
        include vvdp.inc


        DefData     IMPORT,SWAP,C
        ULONG       TKSSBase            ;required for SSToDS macro
        PBYTE       pPhysVRAM
        EndData


        DefData     PRIVATE,SWAPINST,C
        public      abColorXlate
        public      abPlaneOrder
        abColorXlate    db  16 dup (0)  ;last translate table received
        abPlaneOrder    db  0,1,2,3     ;optimal plane order for last table
        ULONG       flInvertMask,0
        EndData


        DefData     PRIVATE,SWAP,C
        public      ausCGAMap,aulXGAMap,abXlatTbl,aulPalTbl

;/*
;**
;**   CGA 320x200 modes use 2 bits per pixel in a single plane, but
;**   the closest DIB format PM supports is 4 bits per pixel, so we have:
;**
;**     CGA
;**     Format      DIB Format          DIB WORD Format
;**     ----------  -----------------   ----------------
;**     7      0    7      0 7      0  15              0
;**     AABBCCDD    00AA00BB 00CC00DD   00CC00DD00AA00BB
;**
;**   Note that when horizontal doubling is performed for this mode, one
;**   byte of CGA data becomes one DWORD rather than one WORD of DIB data,
;**   and to create the DWORD from the WORD, I do the following:
;**
;**                                        31              15       7      0
;**     1. Copy low 16 bits to high:        00CC00DD00AA00BB00CC00DD00AA00BB
;**     2. ROR AX,4                         00CC00DD00AA00BB00BB00CC00DD00AA
;**     3. ROL EAX,8                        00AA00BB00BB00CC00DD00AA00CC00DD
;**     4. ROR AX,8                         00AA00BB00BB00CC00CC00DD00DD00AA
;**     5. ROL EAX,4                        00BB00BB00CC00CC00DD00DD00AA00AA
;**     6. ROR AX,8                         00BB00BB00CC00CC00AA00AA00DD00DD
;**     7. ROL EAX,16                       00AA00AA00DD00DD00BB00BB00CC00CC
;**     8. ROR AX,8                         00AA00AA00DD00DD00CC00CC00BB00BB
;**     9. ROL EAX,8                        00DD00DD00CC00CC00BB00BB00AA00AA
;**
;*/

        align   4                                                       ;          
ausCGAMap  label dword  ;table entries are doubled to DWORD align       ;          
        dw      0000h,0000h,0100h,0100h,0200h,0200h,0300h,0300h         ;          
        dw      1000h,1000h,1100h,1100h,1200h,1200h,1300h,1300h         ;          
        dw      2000h,2000h,2100h,2100h,2200h,2200h,2300h,2300h         ;          
        dw      3000h,3000h,3100h,3100h,3200h,3200h,3300h,3300h         ;          
        dw      0001h,0001h,0101h,0101h,0201h,0201h,0301h,0301h         ;          
        dw      1001h,1001h,1101h,1101h,1201h,1201h,1301h,1301h         ;          
        dw      2001h,2001h,2101h,2101h,2201h,2201h,2301h,2301h         ;          
        dw      3001h,3001h,3101h,3101h,3201h,3201h,3301h,3301h         ;          
        dw      0002h,0002h,0102h,0102h,0202h,0202h,0302h,0302h         ;          
        dw      1002h,1002h,1102h,1102h,1202h,1202h,1302h,1302h         ;          
        dw      2002h,2002h,2102h,2102h,2202h,2202h,2302h,2302h         ;          
        dw      3002h,3002h,3102h,3102h,3202h,3202h,3302h,3302h         ;          
        dw      0003h,0003h,0103h,0103h,0203h,0203h,0303h,0303h         ;          
        dw      1003h,1003h,1103h,1103h,1203h,1203h,1303h,1303h         ;          
        dw      2003h,2003h,2103h,2103h,2203h,2203h,2303h,2303h         ;          
        dw      3003h,3003h,3103h,3103h,3203h,3203h,3303h,3303h         ;          
        dw      0010h,0010h,0110h,0110h,0210h,0210h,0310h,0310h         ;          
        dw      1010h,1010h,1110h,1110h,1210h,1210h,1310h,1310h         ;          
        dw      2010h,2010h,2110h,2110h,2210h,2210h,2310h,2310h         ;          
        dw      3010h,3010h,3110h,3110h,3210h,3210h,3310h,3310h         ;          
        dw      0011h,0011h,0111h,0111h,0211h,0211h,0311h,0311h         ;          
        dw      1011h,1011h,1111h,1111h,1211h,1211h,1311h,1311h         ;          
        dw      2011h,2011h,2111h,2111h,2211h,2211h,2311h,2311h         ;          
        dw      3011h,3011h,3111h,3111h,3211h,3211h,3311h,3311h         ;          
        dw      0012h,0012h,0112h,0112h,0212h,0212h,0312h,0312h         ;          
        dw      1012h,1012h,1112h,1112h,1212h,1212h,1312h,1312h         ;          
        dw      2012h,2012h,2112h,2112h,2212h,2212h,2312h,2312h         ;          
        dw      3012h,3012h,3112h,3112h,3212h,3212h,3312h,3312h         ;          
        dw      0013h,0013h,0113h,0113h,0213h,0213h,0313h,0313h         ;          
        dw      1013h,1013h,1113h,1113h,1213h,1213h,1313h,1313h         ;          
        dw      2013h,2013h,2113h,2113h,2213h,2213h,2313h,2313h         ;          
        dw      3013h,3013h,3113h,3113h,3213h,3213h,3313h,3313h         ;          
        dw      0020h,0020h,0120h,0120h,0220h,0220h,0320h,0320h         ;          
        dw      1020h,1020h,1120h,1120h,1220h,1220h,1320h,1320h         ;          
        dw      2020h,2020h,2120h,2120h,2220h,2220h,2320h,2320h         ;          
        dw      3020h,3020h,3120h,3120h,3220h,3220h,3320h,3320h         ;          
        dw      0021h,0021h,0121h,0121h,0221h,0221h,0321h,0321h         ;          
        dw      1021h,1021h,1121h,1121h,1221h,1221h,1321h,1321h         ;          
        dw      2021h,2021h,2121h,2121h,2221h,2221h,2321h,2321h         ;          
        dw      3021h,3021h,3121h,3121h,3221h,3221h,3321h,3321h         ;          
        dw      0022h,0022h,0122h,0122h,0222h,0222h,0322h,0322h         ;          
        dw      1022h,1022h,1122h,1122h,1222h,1222h,1322h,1322h         ;          
        dw      2022h,2022h,2122h,2122h,2222h,2222h,2322h,2322h         ;          
        dw      3022h,3022h,3122h,3122h,3222h,3222h,3322h,3322h         ;          
        dw      0023h,0023h,0123h,0123h,0223h,0223h,0323h,0323h         ;          
        dw      1023h,1023h,1123h,1123h,1223h,1223h,1323h,1323h         ;          
        dw      2023h,2023h,2123h,2123h,2223h,2223h,2323h,2323h         ;          
        dw      3023h,3023h,3123h,3123h,3223h,3223h,3323h,3323h         ;          
        dw      0030h,0030h,0130h,0130h,0230h,0230h,0330h,0330h         ;          
        dw      1030h,1030h,1130h,1130h,1230h,1230h,1330h,1330h         ;          
        dw      2030h,2030h,2130h,2130h,2230h,2230h,2330h,2330h         ;          
        dw      3030h,3030h,3130h,3130h,3230h,3230h,3330h,3330h         ;          
        dw      0031h,0031h,0131h,0131h,0231h,0231h,0331h,0331h         ;          
        dw      1031h,1031h,1131h,1131h,1231h,1231h,1331h,1331h         ;          
        dw      2031h,2031h,2131h,2131h,2231h,2231h,2331h,2331h         ;          
        dw      3031h,3031h,3131h,3131h,3231h,3231h,3331h,3331h         ;          
        dw      0032h,0032h,0132h,0132h,0232h,0232h,0332h,0332h         ;          
        dw      1032h,1032h,1132h,1132h,1232h,1232h,1332h,1332h         ;          
        dw      2032h,2032h,2132h,2132h,2232h,2232h,2332h,2332h         ;          
        dw      3032h,3032h,3132h,3132h,3232h,3232h,3332h,3332h         ;          
        dw      0033h,0033h,0133h,0133h,0233h,0233h,0333h,0333h         ;          
        dw      1033h,1033h,1133h,1133h,1233h,1233h,1333h,1333h         ;          
        dw      2033h,2033h,2133h,2133h,2233h,2233h,2333h,2333h         ;          
        dw      3033h,3033h,3133h,3133h,3233h,3233h,3333h,3333h         ;          

EVENFASTER = 0  ;disable for now because BITMAP operations in the       ;          
                ;DISPLAY Driver is too slow to keep up with the         ;          
                ;change requests                                        ;          
IF EVENFASTER                                                           ;          

;/*
;**   CGA to VGA Pixel Converion with Pixel Doubling Using DIB Dword Format
;**   ======================================================================
;**   CGA 320x200 modes use 2 bits per pixel in a single plane, but
;**   the closest DIB format PM supports is 4 bits per pixel, so we have:
;**
;**
;**     CGA Format           DIB DWORD Format
;**     ----------           --------------------------------
;**     7      0            31              15       7      0
;**     AABBCCDD             00DD00DD00CC00CC00BB00BB00AA00AA
;**
;**   Note that when horizontal doubling is performed for CGA 2 bits per pixel
;**   mode, one byte of CGA data becomes one DWORD rather than one WORD of DIB
;**   data.  To create the DWORD from the WORD, use the CGA byte as an index
;**   into the DWORD translation array:  aulCGADIB32
;**
;**   aulCGADIB32 is a 256 element array of DWords that, when indexed with a
;**   byte of CGA 4-color pixels (2 bits/pixel), will return the DWord that
;**   has each pixel expanded to 4 bits and then expanded again to 8 bits
;**   for pixel doubling and all the appropriate nibble re-shuffling done for
;**   representation in the DIB DWord format.
;**
;*/

        align 4
aulCGADIB32 label dword
        dd      00000000H,11000000H,22000000H,33000000H
        dd      00110000H,11110000H,22110000H,33110000H
        dd      00220000H,11220000H,22220000H,33220000H
        dd      00330000H,11330000H,22330000H,33330000H
        dd      00001100H,11001100H,22001100H,33001100H
        dd      00111100H,11111100H,22111100H,33111100H
        dd      00221100H,11221100H,22221100H,33221100H
        dd      00331100H,11331100H,22331100H,33331100H
        dd      00002200H,11002200H,22002200H,33002200H
        dd      00112200H,11112200H,22112200H,33112200H
        dd      00222200H,11222200H,22222200H,33222200H
        dd      00332200H,11332200H,22332200H,33332200H
        dd      00003300H,11003300H,22003300H,33003300H
        dd      00113300H,11113300H,22113300H,33113300H
        dd      00223300H,11223300H,22223300H,33223300H
        dd      00333300H,11333300H,22333300H,33333300H
        dd      00000011H,11000011H,22000011H,33000011H
        dd      00110011H,11110011H,22110011H,33110011H
        dd      00220011H,11220011H,22220011H,33220011H
        dd      00330011H,11330011H,22330011H,33330011H
        dd      00001111H,11001111H,22001111H,33001111H
        dd      00111111H,11111111H,22111111H,33111111H
        dd      00221111H,11221111H,22221111H,33221111H
        dd      00331111H,11331111H,22331111H,33331111H
        dd      00002211H,11002211H,22002211H,33002211H
        dd      00112211H,11112211H,22112211H,33112211H
        dd      00222211H,11222211H,22222211H,33222211H
        dd      00332211H,11332211H,22332211H,33332211H
        dd      00003311H,11003311H,22003311H,33003311H
        dd      00113311H,11113311H,22113311H,33113311H
        dd      00223311H,11223311H,22223311H,33223311H
        dd      00333311H,11333311H,22333311H,33333311H
        dd      00000022H,11000022H,22000022H,33000022H
        dd      00110022H,11110022H,22110022H,33110022H
        dd      00220022H,11220022H,22220022H,33220022H
        dd      00330022H,11330022H,22330022H,33330022H
        dd      00001122H,11001122H,22001122H,33001122H
        dd      00111122H,11111122H,22111122H,33111122H
        dd      00221122H,11221122H,22221122H,33221122H
        dd      00331122H,11331122H,22331122H,33331122H
        dd      00002222H,11002222H,22002222H,33002222H
        dd      00112222H,11112222H,22112222H,33112222H
        dd      00222222H,11222222H,22222222H,33222222H
        dd      00332222H,11332222H,22332222H,33332222H
        dd      00003322H,11003322H,22003322H,33003322H
        dd      00113322H,11113322H,22113322H,33113322H
        dd      00223322H,11223322H,22223322H,33223322H
        dd      00333322H,11333322H,22333322H,33333322H
        dd      00000033H,11000033H,22000033H,33000033H
        dd      00110033H,11110033H,22110033H,33110033H
        dd      00220033H,11220033H,22220033H,33220033H
        dd      00330033H,11330033H,22330033H,33330033H
        dd      00001133H,11001133H,22001133H,33001133H
        dd      00111133H,11111133H,22111133H,33111133H
        dd      00221133H,11221133H,22221133H,33221133H
        dd      00331133H,11331133H,22331133H,33331133H
        dd      00002233H,11002233H,22002233H,33002233H
        dd      00112233H,11112233H,22112233H,33112233H
        dd      00222233H,11222233H,22222233H,33222233H
        dd      00332233H,11332233H,22332233H,33332233H
        dd      00003333H,11003333H,22003333H,33003333H
        dd      00113333H,11113333H,22113333H,33113333H
        dd      00223333H,11223333H,22223333H,33223333H
        dd      00333333H,11333333H,22333333H,33333333H
ENDIF                                                                   ;          

;/*
;**
;**   EGA/VGA multi-plane modes use 1 bit per pixel across 4 planes (0/1/2/3):
;**
;**         EGA/VGA
;**         Format      DIB Format
;**         ----------  -----------------------------------
;**         7      0    7      0 7      0 7      0 7      0
;**     0:  ABCDEFGH    000A000B 000C000D 000E000F 000G000H
;**     1:  ABCDEFGH    00A000B0 00C000D0 00E000F0 00G000H0
;**     2:  ABCDEFGH    0A000B00 0C000D00 0E000F00 0G000H00
;**     3:  ABCDEFGH    A000B000 C000D000 E000F000 G000H000
;**                     AAAABBBB CCCCDDDD EEEEFFFF GGGGHHHH
;**
;**         DIB DWORD Format (derived from table below):
;**         --------------------------------------------
;**        31                              0
;**         GGGGHHHHEEEEFFFFCCCCDDDDAAAABBBB
;**
;*/

        aulXGAMap   dd      00000000h,01000000h,10000000h,11000000h
                    dd      00010000h,01010000h,10010000h,11010000h
                    dd      00100000h,01100000h,10100000h,11100000h
                    dd      00110000h,01110000h,10110000h,11110000h
                    dd      00000100h,01000100h,10000100h,11000100h
                    dd      00010100h,01010100h,10010100h,11010100h
                    dd      00100100h,01100100h,10100100h,11100100h
                    dd      00110100h,01110100h,10110100h,11110100h
                    dd      00001000h,01001000h,10001000h,11001000h
                    dd      00011000h,01011000h,10011000h,11011000h
                    dd      00101000h,01101000h,10101000h,11101000h
                    dd      00111000h,01111000h,10111000h,11111000h
                    dd      00001100h,01001100h,10001100h,11001100h
                    dd      00011100h,01011100h,10011100h,11011100h
                    dd      00101100h,01101100h,10101100h,11101100h
                    dd      00111100h,01111100h,10111100h,11111100h
                    dd      00000001h,01000001h,10000001h,11000001h
                    dd      00010001h,01010001h,10010001h,11010001h
                    dd      00100001h,01100001h,10100001h,11100001h
                    dd      00110001h,01110001h,10110001h,11110001h
                    dd      00000101h,01000101h,10000101h,11000101h
                    dd      00010101h,01010101h,10010101h,11010101h
                    dd      00100101h,01100101h,10100101h,11100101h
                    dd      00110101h,01110101h,10110101h,11110101h
                    dd      00001001h,01001001h,10001001h,11001001h
                    dd      00011001h,01011001h,10011001h,11011001h
                    dd      00101001h,01101001h,10101001h,11101001h
                    dd      00111001h,01111001h,10111001h,11111001h
                    dd      00001101h,01001101h,10001101h,11001101h
                    dd      00011101h,01011101h,10011101h,11011101h
                    dd      00101101h,01101101h,10101101h,11101101h
                    dd      00111101h,01111101h,10111101h,11111101h
                    dd      00000010h,01000010h,10000010h,11000010h
                    dd      00010010h,01010010h,10010010h,11010010h
                    dd      00100010h,01100010h,10100010h,11100010h
                    dd      00110010h,01110010h,10110010h,11110010h
                    dd      00000110h,01000110h,10000110h,11000110h
                    dd      00010110h,01010110h,10010110h,11010110h
                    dd      00100110h,01100110h,10100110h,11100110h
                    dd      00110110h,01110110h,10110110h,11110110h
                    dd      00001010h,01001010h,10001010h,11001010h
                    dd      00011010h,01011010h,10011010h,11011010h
                    dd      00101010h,01101010h,10101010h,11101010h
                    dd      00111010h,01111010h,10111010h,11111010h
                    dd      00001110h,01001110h,10001110h,11001110h
                    dd      00011110h,01011110h,10011110h,11011110h
                    dd      00101110h,01101110h,10101110h,11101110h
                    dd      00111110h,01111110h,10111110h,11111110h
                    dd      00000011h,01000011h,10000011h,11000011h
                    dd      00010011h,01010011h,10010011h,11010011h
                    dd      00100011h,01100011h,10100011h,11100011h
                    dd      00110011h,01110011h,10110011h,11110011h
                    dd      00000111h,01000111h,10000111h,11000111h
                    dd      00010111h,01010111h,10010111h,11010111h
                    dd      00100111h,01100111h,10100111h,11100111h
                    dd      00110111h,01110111h,10110111h,11110111h
                    dd      00001011h,01001011h,10001011h,11001011h
                    dd      00011011h,01011011h,10011011h,11011011h
                    dd      00101011h,01101011h,10101011h,11101011h
                    dd      00111011h,01111011h,10111011h,11111011h
                    dd      00001111h,01001111h,10001111h,11001111h
                    dd      00011111h,01011111h,10011111h,11011111h
                    dd      00101111h,01101111h,10101111h,11101111h
                    dd      00111111h,01111111h,10111111h,11111111h

;/*
;**
;**   The following table contains 24 16-byte subtables.  Each subtable
;**   represents the effective color translation when the 4 color planes
;**   (0/1/2/3) are copied in some other unique order (ie, 0/1/2/3, or 1/0/2/3,
;**   or 1/2/0/3, and so on).  The high nibbles of the first 4 bytes of
;**   each subtable contain the order, and the low nibbles of all 16 bytes
;**   contain the color translation that would be achieved.  There are only 24
;**   subtables because that's the exact number of unique orderings of 0/1/2/3.
;**
;*/

        abXlatTbl   db      00h,11h,22h,33h,04h,05h,06h,07h
                    db      08h,09h,0Ah,0Bh,0Ch,0Dh,0Eh,0Fh
                    db      10h,02h,21h,33h,04h,06h,05h,07h
                    db      08h,0Ah,09h,0Bh,0Ch,0Eh,0Dh,0Fh
                    db      10h,22h,04h,36h,01h,03h,05h,07h
                    db      08h,0Ah,0Ch,0Eh,09h,0Bh,0Dh,0Fh
                    db      20h,14h,02h,36h,01h,05h,03h,07h
                    db      08h,0Ch,0Ah,0Eh,09h,0Dh,0Bh,0Fh
                    db      20h,04h,11h,35h,02h,06h,03h,07h
                    db      08h,0Ch,09h,0Dh,0Ah,0Eh,0Bh,0Fh
                    db      00h,21h,14h,35h,02h,03h,06h,07h
                    db      08h,09h,0Ch,0Dh,0Ah,0Bh,0Eh,0Fh
                    db      00h,21h,34h,15h,08h,09h,0Ch,0Dh
                    db      02h,03h,06h,07h,0Ah,0Bh,0Eh,0Fh
                    db      20h,04h,31h,15h,08h,0Ch,09h,0Dh
                    db      02h,06h,03h,07h,0Ah,0Eh,0Bh,0Fh
                    db      20h,34h,08h,1Ch,01h,05h,09h,0Dh
                    db      02h,06h,0Ah,0Eh,03h,07h,0Bh,0Fh
                    db      30h,28h,04h,1Ch,01h,09h,05h,0Dh
                    db      02h,0Ah,06h,0Eh,03h,0Bh,07h,0Fh
                    db      30h,08h,21h,19h,04h,0Ch,05h,0Dh
                    db      02h,0Ah,03h,0Bh,06h,0Eh,07h,0Fh
                    db      00h,31h,28h,19h,04h,05h,0Ch,0Dh
                    db      02h,03h,0Ah,0Bh,06h,07h,0Eh,0Fh
                    db      00h,31h,18h,29h,02h,03h,0Ah,0Bh
                    db      04h,05h,0Ch,0Dh,06h,07h,0Eh,0Fh
                    db      30h,08h,11h,29h,02h,0Ah,03h,0Bh
                    db      04h,0Ch,05h,0Dh,06h,0Eh,07h,0Fh
                    db      30h,18h,02h,2Ah,01h,09h,03h,0Bh
                    db      04h,0Ch,06h,0Eh,05h,0Dh,07h,0Fh
                    db      10h,32h,08h,2Ah,01h,03h,09h,0Bh
                    db      04h,06h,0Ch,0Eh,05h,07h,0Dh,0Fh
                    db      10h,02h,31h,23h,08h,0Ah,09h,0Bh
                    db      04h,06h,05h,07h,0Ch,0Eh,0Dh,0Fh
                    db      00h,11h,32h,23h,08h,09h,0Ah,0Bh
                    db      04h,05h,06h,07h,0Ch,0Dh,0Eh,0Fh
                    db      20h,14h,32h,06h,08h,0Ch,0Ah,0Eh
                    db      01h,05h,03h,07h,09h,0Dh,0Bh,0Fh
                    db      10h,22h,34h,06h,08h,0Ah,0Ch,0Eh
                    db      01h,03h,05h,07h,09h,0Bh,0Dh,0Fh
                    db      10h,32h,28h,0Ah,04h,06h,0Ch,0Eh
                    db      01h,03h,09h,0Bh,05h,07h,0Dh,0Fh
                    db      30h,18h,22h,0Ah,04h,0Ch,06h,0Eh
                    db      01h,09h,03h,0Bh,05h,0Dh,07h,0Fh
                    db      30h,28h,14h,0Ch,02h,0Ah,06h,0Eh
                    db      01h,09h,05h,0Dh,03h,0Bh,07h,0Fh
                    db      20h,34h,18h,0Ch,02h,06h,0Ah,0Eh
                    db      01h,05h,09h,0Dh,03h,07h,0Bh,0Fh

;/*
;**
;**   This table simply maps the EGA/VGA palette registers, which have
;**   the following format:
;**
;**     7      0
;**     xxrgbRGB    where R, G and B are primary intensity (2/3 of maximum)
;**                 and r, g and b are secondary intensity (1/3 of maximum)
;**
;**   to the standard RGB format:
;**
;**     7      0 7      0 7      0      where XXXXXXXX = (x * 85) + (X * 170);
;**     BBBBBBBB GGGGGGGG RRRRRRRR      note 85 = (255*1/3) and 170 = (255*2/3)
;**
;**   which, when encoded as a DWORD (the format of the table), is:
;**
;**    31                              0
;**     00000000RRRRRRRRGGGGGGGGBBBBBBBB
;**
;*/

        aulPalTbl   dd      00000000h,000000AAh,0000AA00h,0000AAAAh
                    dd      00AA0000h,00AA00AAh,00AAAA00h,00AAAAAAh
                    dd      00000055h,000000FFh,0000AA55h,0000AAFFh
                    dd      00AA0055h,00AA00FFh,00AAAA55h,00AAAAFFh
                    dd      00005500h,000055AAh,0000FF00h,0000FFAAh
                    dd      00AA5500h,00AA55AAh,00AAFF00h,00AAFFAAh
                    dd      00005555h,000055FFh,0000FF55h,0000FFFFh
                    dd      00AA5555h,00AA55FFh,00AAFF55h,00AAFFFFh
                    dd      00550000h,005500AAh,0055AA00h,0055AAAAh
                    dd      00FF0000h,00FF00AAh,00FFAA00h,00FFAAAAh
                    dd      00550055h,005500FFh,0055AA55h,0055AAFFh
                    dd      00FF0055h,00FF00FFh,00FFAA55h,00FFAAFFh
                    dd      00555500h,005555AAh,0055FF00h,0055FFAAh
                    dd      00FF5500h,00FF55AAh,00FFFF00h,00FFFFAAh
                    dd      00555555h,005555FFh,0055FF55h,0055FFFFh
                    dd      00FF5555h,00FF55FFh,00FFFF55h,00FFFFFFh
        EndData


        DefCode     IMPORT,SWAP,PASCAL
       IFDEF EGAVGA
        DefFn       vvSetCopyState
       ENDIF
        EndCode


        DefCode     PRIVATE,SWAP,PASCAL

        DefFn       vvCopyLVBToLVB
        DefFn       vvCopyBlankToLVB
        DefFn       vvCopyGraphicsToBitmap
       IFDEF EGAVGA
        DefFn       vvCopyGraphicsToDDBitmap
        DefFn       vvAnalyzeColors
       ENDIF
        DefFn       vvConvertPalette
        DefFn       vvFillShadowBuffer
        DefFn       vvDiffShadowBuffer
       IFDEF EGAVGA
        DefFn       vvDiffPhysPage
       ENDIF


;/***************************************************************************
;*
;* FUNCTION NAME = vvCopyLVBToLVB()
;*
;* DESCRIPTION   = Copy rectangle of LVB to specfied LVB.
;*
;* INPUT         = hvdm  -> VDM
;*                 prcl  -> rectangle description
;*                 pBuff -> destination LVB buffer
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvCopyLVBToLVB
        ArgVar  hvdm,HVDM
        ArgVar  prcl,PRECTL
        ArgVar  pBuff,PVOID
        ArgVar  bSize,ULONG                                             ;          

        EnterProc
        push    ebx
        push    esi
        push    edi

        mov     ebx,[hvdm]
        mov     esi,VDMData.apPlane[BANK0][PLANE0][ebx]                 ;          
        add     esi,VDMData.offPhysVRAMVisible[ebx]
        mov     edi,[pBuff]

        mov     eax,VDMData.vvMode.vvm_nCols[ebx]
        add     eax,eax                 ;adjust for char/attr bytes
        mov     ebx,[prcl]

        mov     ecx,[ebx].yTop          ;compute rect offset
        imul    ecx,eax

        cmp     ecx,[bSize]             ;overflow condition?            ;          
        ja      short vvclvb_exit       ;                               ;          

        add     esi,ecx
        add     edi,ecx                 ;src/dst adjusted to correct row
        mov     ecx,[ebx].xLeft
        add     ecx,ecx                 ;adjust for char/attr bytes
        add     esi,ecx
        add     edi,ecx                 ;src/dst adjusted to correct column
        mov     ecx,[ebx].yBottom
        sub     ecx,[ebx].yTop
        inc     ecx                     ;ECX == # rows
        jle     short vvclvb_exit       ;invalid rectangle
        mov     edx,[ebx].xRight
        sub     edx,[ebx].xLeft
        inc     edx                     ;EDX == # columns
        jle     short vvclvb_exit       ;invalid rectangle

        sub     eax,edx
        sub     eax,edx                 ;EAX == adjustment after each row-copy
        mov     ebx,ecx                 ;EBX == # rows (now that EBX is free)
        align   4                                                       ;          
vvclvb_rowloop:
        mov     ecx,edx                 ;get # columns
        shr     ecx,1                   ;convert to # dwords
        rep     movsd
        rcl     ecx,1
        rep     movsw
        add     esi,eax
        add     edi,eax
        dec     ebx                     ;EBX == # rows remaining
        jnz     short vvclvb_rowloop

vvclvb_exit:
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvCopyLVBToLVB


;/***************************************************************************
;*
;* FUNCTION NAME = vvCopyBlankToLVB()
;*
;* DESCRIPTION   = Copy blank rectangle to specified LVB
;*                 Note that this is almost identical to CopyLVBToLVB, except that
;*                 the source is implied to be an imaginary blank video buffer.
;*
;* INPUT         = hvdm  -> VDM
;*                 prcl  -> rectangle description
;*                 pBuff -> destination LVB buffer
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvCopyBlankToLVB
        ArgVar  hvdm,HVDM
        ArgVar  prcl,PRECTL
        ArgVar  pBuff,PVOID
        ArgVar  bSize,ULONG                                             ;          

        EnterProc
        push    ebx
        push    esi
        push    edi

        mov     ebx,[hvdm]
        mov     edi,[pBuff]

        sub     eax,eax
        cmp     VDMData.mstateVideo[ebx],MEMORY_TEXT
        jne     short vvcblnk_cont
        mov     eax,07200720h           ;a DWORD of default char/attr
vvcblnk_cont:
        mov     esi,VDMData.vvMode.vvm_nCols[ebx]
        add     esi,esi                 ;adjust for char/attr bytes
        mov     ebx,[prcl]

        mov     ecx,[ebx].yTop          ;compute rect offset
        imul    ecx,esi

        cmp     ecx,[bSize]             ;overflow condition?            ;          
        ja      short vvcblnk_exit      ;                               ;          

        add     edi,ecx                 ;src/dst adjusted to correct row
        mov     ecx,[ebx].xLeft
        add     ecx,ecx                 ;adjust for char/attr bytes
        add     edi,ecx                 ;src/dst adjusted to correct column
        mov     ecx,[ebx].yBottom
        sub     ecx,[ebx].yTop
        inc     ecx                     ;ECX == # rows
        jle     short vvcblnk_exit      ;invalid rectangle
        mov     edx,[ebx].xRight
        sub     edx,[ebx].xLeft
        inc     edx                     ;EDX == # columns
        jle     short vvcblnk_exit      ;invalid rectangle

        sub     esi,edx
        sub     esi,edx                 ;ESI == adjustment after each row-copy
        mov     ebx,ecx                 ;EBX == # rows (now that EBX is free)
        align   4                                                       ;          
vvcblnk_rowloop:
        mov     ecx,edx                 ;get # columns
        shr     ecx,1                   ;convert to # dwords
        rep     stosd
        rcl     ecx,1
        rep     stosw
        add     edi,esi
        dec     ebx                     ;EBX == # rows remaining
        jnz     vvcblnk_rowloop

vvcblnk_exit:
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvCopyBlankToLVB


IFDEF   SVGA

;/***************************************************************************
;*
;* FUNCTION NAME = vvCopyGraphicsToBitmap256()
;*
;* DESCRIPTION   = Copy graphics rectangle in bitmap format.
;*
;*                 We only support whole scanline copies (ie, MODE_SUP_PARTIALSCAN is
;*                 never set).  Furthermore, we only support scaling in certain paths,
;*                 and those paths should match the conditions where vvUpdateModeData
;*                 sets MODE_SUP_XSCALE2 or MODE_SUP_YSCALE2.
;*
;*                 To circumvent the situation where a VDM is windowed while a 256
;*                 colour mode set is in effect, and before the virtual buffer
;*                 allocation has completed, a check must be made on the linear
;*                 buffer pointer. If a null entry is found, the copy will be aborted.
;*
;* INPUT         = hvdm    -> VDM
;*                 pvvr    -> copy information
;*                 pBitmap -> destination bitmap buffer
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        DefFn       vvCopyGraphicsToBitmap256

        align   4

Procedure vvCopyGraphicsToBitmap256
        ArgVar  hvdm,HVDM
        ArgVar  pvvr,PVVRECT
        ArgVar  pBitmap,PVOID

        LocalVar nbScanVirt,ULONG                              ;           screen width in bytes
        LocalVar nbScanBytes,ULONG                             ;           bitmap width in bytes

        EnterProc

        push    ebx
        push    esi
        push    edi
        mov     ebx,[hvdm]
        mov     esi,[pvvr]

        mov     eax,VDMData.vvMode.vvm_nCols[ebx]                       ;             
        ; take into account the pixel width.                            ;          
        shr     eax, 3                                                  ;          
        mul     VDMData.vvMode.vvm_nBitCount[ebx]                       ;          
        mov     [nbScanBytes],eax                                       ;          
        mov     [nbScanVirt],eax                                        ;          
        mov     ecx,VDMData.VdmSVGA.ulLogicalBytes[ebx] ;              /*          */
        cmp     eax,ecx                                                 ;             
        jae     short @F                                                ;             
        ; logical line length greater than hres in bytes, modify nbScanVirt
        mov     [nbScanVirt],ecx                                        ;             

@@:     mov     eax,[nbScanVirt]
        mov     ecx,eax
        mov     edx,[esi].vvr_rcl.yBottom
        sub     edx,[esi].vvr_rcl.yTop
        js      short vvcgb2_exit

        mul     edx                             ; 
        add     eax,ecx                         ; 

        mov     edi,eax                         ;no of bytes to copy
        mov     eax,[esi].vvr_rcl.yTop
        mul     [nbScanVirt]
        mov     esi,VDMData.pLinearBuffer[ebx]  ;ESI -> saved video buffer
        or      esi,esi
        jz      short vvcgb2_exit
        mov     edx,edi
        add     esi,eax
        add     esi,edx
        mov     eax,ecx
        sub     esi,eax
        mov     edi,[pBitmap]                   ;EDI -> bitmap storage

        mov     ecx,[nbScanBytes]               ;          
        mov     ebx,eax                         ;EBX = nbScanVirt
        mov     eax,ecx                         ;EAX = ECX = nCols

        align   4
vvcgb2_copy:
        rep     movsb
        mov     ecx,eax
        sub     esi,eax
        sub     esi,ebx
        sub     edx,ebx
        jnz     vvcgb2_copy

vvcgb2_exit:
        pop     edi
        pop     esi
        pop     ebx

        ExitProc

EndProc   vvCopyGraphicsToBitmap256

ENDIF   ;SVGA defined


;/***************************************************************************
;*
;* FUNCTION NAME = vvCopyGraphicsToBitmap()
;*
;* DESCRIPTION   = Copy graphics rectangle in bitmap format
;*                 We only support whole scanline copies (ie, MODE_SUP_PARTIALSCAN is
;*                 never set).  Furthermore, we only support scaling in certain paths,
;*                 and those paths should match the conditions where vvUpdateModeData
;*                 sets MODE_SUP_XSCALE2 or MODE_SUP_YSCALE2.
;*
;*                 FLAG: 24-Jan-90:
;*                    The size of the destination buffer needs to be verified, taking into
;*                    account any scaling requested.  At this time, we are relying on the
;*                    integrity of the Shield to avoid taking a fatal fault here....
;*
;*
;* INPUT         = hvdm    -> VDM
;*                 pvvr    -> copy information
;*                 pBitmap -> destination bitmap buffer
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvCopyGraphicsToBitmap
        ArgVar  hvdm,HVDM
        ArgVar  pvvr,PVVRECT
        ArgVar  pBitmap,PVOID

        LocalVar rowCur,LONG
        LocalVar rowTop,LONG
        LocalVar nbScan,ULONG
        LocalVar nbScanVirt,ULONG
        LocalVar nbScanCur,ULONG
        LocalVar incBitmap,ULONG
        LocalVar offOddEven,ULONG
        LocalVar iPlaneMax,ULONG
        LocalVar pPlane,16              ;plane pointers (current positions)
        LocalVar pPlaneInvalid,16       ;plane pointers (first invalid positions)
        LocalVar xScale,ULONG
        LocalVar yScale,ULONG
        LocalVar yScaleCur,ULONG
        IFDEF SVGA                      ;                                          
        LocalVar iBankCur,ULONG         ;                                          
        ENDIF                           ;                                          

        EnterProc

        push    ebx
        push    esi
        push    edi

        mov     ebx,[hvdm]
        mov     esi,[pvvr]

        mov     eax,[esi].vvr_rcl.yBottom
        mov     [rowCur],eax
        mov     eax,[esi].vvr_rcl.yTop
        mov     [rowTop],eax

        mov     eax,[esi].vvr_cx
        cdq
        div     VDMData.vvMode.vvm_nCols[ebx]
        mov     [xScale],eax
        mov     eax,[esi].vvr_cy
        cdq
        div     VDMData.vvMode.vvm_nRows[ebx]
        mov     [yScale],eax

        mov     eax,VDMData.vvMode.vvm_nCols[ebx]
        mul     VDMData.nBitsPixel[ebx]
        shr     eax,3                   ;divide # bits by 8 to give # bytes
        mov     [nbScan],eax            ;# bytes per scan line
        mov     [nbScanVirt],eax        ;# bytes per logical scan line
        mul     [rowCur]
        mov     edx,eax                 ;EDX == offset to bottom row to copy
        mov     esi,VDMData.nPlanes[ebx]
        mov     [incBitmap],esi
        dec     esi
        mov     [iPlaneMax],esi         ;ESI == plane index
        mov     eax,VDMData.offOddBank[ebx]
        mov     [offOddEven],eax
        IFDEF SVGA                      ;                                          
        mov     iBankCur,edx            ;                                          
        shr     iBankCur,16             ;                                          
        inc     iBankCur                ;now has no of 64k banks                   
        cmp     edx,010000H             ;more than one bank per plane?             
        jbe     @F                      ;                                          
        movzx   edx,dx                  ;no of bytes in last bank                  
        push    eax                     ;                                          
        push    edx                     ;save no of bytes                          
        mov     eax,edx                 ;calculate new [RowTop]                    
        cdq                             ;                                          
        div     [nbScanVirt]            ;                                          
        sub     eax,[rowCur]            ;                                          
        neg     eax                     ;                                          
        mov     [rowTop],eax            ;                                          
        pop     edx                     ;                                          
        pop     eax                     ;                                          
@@:                                     ;                                          
        ENDIF                           ;                                          
        or      eax,eax
        jz      short vvcgb_ptrsetup    ;not a CGA graphics mode
        sub     ecx,ecx                 ;this IS a CGA graphics mode
        mov     eax,[rowCur]
        shr     eax,1
        rcr     cx,3
        mul     [nbScanVirt]
        add     eax,ecx
        mov     edx,eax                 ;EDX == offset to bottom row to copy
        mov     eax,VDMData.nBitsPixel[ebx]
        mov     [incBitmap],eax
        jmp     short vvcgb_ptrsetup    ; 

        IFDEF SVGA                      ;                                          
vvcgb_banksetup:                        ;                                          
        mov     eax,010000H             ;                                          
        cdq                             ;calculate new [RowTop]                    
        div     [nbScanVirt]            ;                                          
        sub     eax,[rowCur]            ;                                          
        dec     eax                     ;                                          
        neg     eax                     ;                                          
        mov     [rowTop],eax            ;                                          
        mov     eax,[nbScan]            ;                                          
        mul     [rowCur]                ;                                          
        mov     edx,eax                 ;EDX - set up to do a whole 64k bank
        mov     ebx,[hvdm]              ;                                          
        mov     esi,[iPlaneMax]         ;                                          
        mov     [pBitmap],edi           ;save current position                     
        ENDIF                           ;                                          

vvcgb_ptrsetup:
        IFDEF SVGA                      ;                                          
        mov     edi,iBankCur            ;                                          
        dec     edi                     ;                                          
        shl     edi,4                   ;                                          
        add     edi,ebx                 ;                                          
        mov     eax,VDMData.apPlane[edi][esi*4]                         ;          
        mov     edi,VDMData.anpgPlane[edi][esi*4]                       ;          
        ELSE                            ;                                          
        mov     eax,VDMData.apPlane[BANK0][ebx][esi*4]                  ;          
        mov     edi,VDMData.anpgPlane[BANK0][ebx][esi*4]                ;          
        ENDIF                           ;                                          
        shl     edi,12                  ;convert # pages to # bytes
        add     edi,eax
        add     eax,VDMData.offPhysVRAMVisible[ebx]
        add     eax,edx                 ;advance to bottom of req. rectangle
        mov     dword ptr pPlane[esi*4],eax
       IFDEF EGAVGA                     ;FLAG: 24-Sep-90  More code could be
        mov     ecx,esi                 ;eliminated in the MONO and CGA drivers
        mov     ch,1
        shl     ch,cl                   ;CH == plane bit
        test    VDMData.aregATCData[ebx+REG_ATCPLANEENABLE],ch
        jnz     short vvcgb_enabled
        sub     edi,edi
vvcgb_enabled:
       ENDIF
        mov     dword ptr pPlaneInvalid[esi*4],edi
        dec     esi
        jns     vvcgb_ptrsetup
        mov     edi,[pBitmap]           ;EDI -> bitmap storage
        align   4                                                       ;          
vvcgb_rowloop:
        mov     eax,[yScale]
        mov     [yScaleCur],eax
        align   4                                                       ;          
vvcgb_duprowloop:
        mov     eax,[rowCur]
        cmp     eax,[rowTop]
        jl      vvcgb_exit              ;exit when all rows processed
        mov     esi,dword ptr pPlane[0]
        mov     ecx,[nbScan]            ;prime # bytes this scanline
        mov     [nbScanCur],ecx         ;(or we can just use ecx, whichever)
        mov     edx,dword ptr pPlaneInvalid[0]
        mov     ebx,[iPlaneMax]         ;EBX == plane index
        cmp     [incBitmap],2
        je      vvcgb_scan2                                             ;          
        cmp     [incBitmap],4
        je      vvcgb_scan4

        lea     eax,[esi+ecx]           ;compute final address
        cmp     eax,edx                 ;can we copy a whole scan line?
        ja      short vvcgb_scan1       ;no
        cmp     [xScale],1              ;yes, scaling?
        ja      short vvcgb_scan1b      ;yes
        rep     movsb                   ;no, so just copy it
        jmp     short vvcgb_scan1end

vvcgb_scan1:
        sub     eax,edx                 ;EAX == # of bytes we can't copy
        cmp     eax,ecx                 ;is it > the size of a scanline?
        jbe     short vvcgb_scan1a      ;no
        mov     eax,ecx
vvcgb_scan1a:
        sub     ecx,eax                 ;reduce count by that amount
        cmp     [xScale],1              ;scaling?
        ja      short vvcgb_scan1c      ;yes
        rep     movsb
        add     esi,eax
        mov     ecx,eax
        sub     al,al                   ;fill the rest with zeros
        rep     stosb
        jmp     short vvcgb_scan1end

vvcgb_scan1b:                           ;do full scanline with scaling
        shr     ecx,1                   ;word aligned it                ;          
        jnc     vvcgb_scan1b_1                                          ;          
        inc     cx                                                      ;          
        jmp     vvcgb_scan1b_2                                          ;          
        align   4                                                       ;          
vvcgb_scan1b_1:                         ;even boundary                  ;          
        lodsb                                                           ;          
        mov     ah,al                                                   ;          
        stosw                                                           ;          
vvcgb_scan1b_2:                         ;odd boudary                    ;          
        lodsb                                                           ;          
        mov     ah,al                                                   ;          
        stosw                                                           ;          
        hploop  vvcgb_scan1b_1                                          ;          
        jmp     short vvcgb_scan1end

vvcgb_scan1c:                           ;do partial scanline with scaling
        jecxz   short vvcgb_scan1e
        push    eax                     ;save # bytes we can't copy
vvcgb_scan1d:
        shr     ecx,1                   ;word aligned it                ;          
        jnc     vvcgb_scan1d_1                                          ;          
        inc     cx                                                      ;          
        jmp     vvcgb_scan1d_2                                          ;          
        align   4                                                       ;          
vvcgb_scan1d_1:                         ;even boundary                  ;          
        lodsb                                                           ;          
        mov     ah,al                                                   ;          
        stosw                                                           ;          
vvcgb_scan1d_2:                         ;even boundary                  ;          
        lodsb                                                           ;          
        mov     ah,al                                                   ;          
        stosw                                                           ;          
        hploop  vvcgb_scan1d_1                                          ;          
        pop     eax
vvcgb_scan1e:
        add     esi,eax
        lea     ecx,[eax*2]
        sub     al,al                   ;fill the rest with zeros
        rep     stosb
vvcgb_scan1end:
        mov     dword ptr pPlane[0],esi ;lastly, update plane pointer
        jmp     vvcgb_ptradjust

vvcgb_scan2:
        cmp     [xScale],1              ;scaling?
        ja      short vvcgb_scan2b      ;yes
        align   4                                                       ;          
vvcgb_scan2a:
        sub     eax,eax
        cmp     esi,edx
        jae     short vvcgb_convert2
        movzx   eax,byte ptr [esi]      ;fetch a byte from the actual plane
vvcgb_convert2:
        inc     esi                     ;advance plane pointer
        movzx   eax,word ptr ausCGAMap[eax*4]   ;instead of [eax*2]     ;          
        stosw
        hploop  vvcgb_scan2a
        mov     dword ptr pPlane[0],esi ;update plane pointer
        jmp     vvcgb_ptradjust                                         ;          

vvcgb_scan2b:
        push    ebx
IF EVENFASTER                                                           ;          
        shr     ecx,1                                                   ;          
        jnc     vvcgb_scan2c                                            ;          
        inc     ecx                                                     ;          
        jmp     short vvcgb_convert2b_half                              ;          
        align   4                                                       ;          
vvcgb_scan2c:                                                           ;          
        sub     ebx,ebx                                                 ;          
        cmp     esi,edx                                                 ;          
        jae     short vvcgb_convert2b                                   ;          
        mov     bl,byte ptr [esi]      ;fetch a byte from the actual plane
        shl     ebx,2                                                   ;          
vvcgb_convert2b:                                                        ;          
        inc     esi                     ;advance plane pointer          ;          
        mov     eax,aulCGADIB32[ebx]                                    ;          
        stosd                                                           ;          
vvcgb_convert2b_half:                                                   ;          
        sub     ebx,ebx                                                 ;          
        cmp     esi,edx                                                 ;          
        jae     short vvcgb_convert2b_1                                 ;          
        mov     bl,byte ptr [esi]      ;fetch a byte from the actual plane
        shl     ebx,2                                                   ;          
vvcgb_convert2b_1:                                                      ;          
        inc     esi                     ;advance plane pointer          ;          
        mov     eax,aulCGADIB32[ebx]                                    ;          
        stosd                                                           ;          
        hploop  vvcgb_scan2c                                            ;          
ELSE                                                                    ;          
        align   4                                                       ;          
vvcgb_scan2c:                                                           ;          
        shr     ecx,1                                                   ;          
        jnc     vvcgb_scan2c_1                                          ;          
        inc     ecx                                                     ;          
        jmp     short vvcgb_convert2b_half                              ;          
        align   4
vvcgb_scan2c_1:
        sub     ebx,ebx
        cmp     esi,edx
        jae     short vvcgb_convert2b
        mov     bl,byte ptr [esi]    ;fetch a byte from the actual plane;          
vvcgb_convert2b:
        inc     esi                     ;advance plane pointer
        shl     ebx,2                                                   ;          
        mov     eax,ausCGAMap[ebx]                                      ;          
        ror     ax,4                    ;begin magic series of rotations
        rol     eax,8                   ;(see discussion near ausCGAMap for
        ror     ax,8                    ; details)
        rol     eax,4
        ror     ax,8
        rol     eax,16
        ror     ax,8
        rol     eax,8                   ;end magic series of rotations
        stosd

vvcgb_convert2b_half:                   ;expand once!                   ;          
                                                                        ;          
        sub     ebx,ebx                                                 ;          
        cmp     esi,edx                                                 ;          
        jae     short vvcgb_convert2b_1                                 ;          
        mov     bl,byte ptr [esi]    ;fetch a byte from the actual plane;          
vvcgb_convert2b_1:                                                      ;          
        inc     esi                     ;advance plane pointer          ;          
        shl     ebx,2                                                   ;          
        mov     eax,ausCGAMap[ebx]                                      ;          
        ror     ax,4                    ;begin magic series of rotations;          
        rol     eax,8                   ;(see discussion near ausCGAMap for
        ror     ax,8                    ; details)                      ;          
        rol     eax,4                                                   ;          
        ror     ax,8                                                    ;          
        rol     eax,16                                                  ;          
        ror     ax,8                                                    ;          
        rol     eax,8                   ;end magic series of rotations  ;          
        stosd                                                           ;          
        hploop  vvcgb_scan2c_1                                          ;          
ENDIF                                                                   ;          
        mov     dword ptr pPlane[0],esi ;update plane pointer
        pop     ebx
        jmp     short vvcgb_ptradjust

vvcgb_scan4:
        mov     ecx,ebx                 ;ECX == plane index
        sub     edx,edx                 ;EDX == next dword for bitmap
vvcgb_plane4:
        mov     esi,dword ptr pPlane[ecx*4]
        sub     eax,eax
        cmp     esi,dword ptr pPlaneInvalid[ecx*4]
        jae     short vvcgb_convert4
        movzx   eax,byte ptr [esi]      ;fetch a byte from the actual plane
vvcgb_convert4:
        inc     dword ptr pPlane[ecx*4]
        mov     eax,aulXGAMap[eax*4]
        shl     eax,cl                  ;adjust for plane
        or      edx,eax                 ;'or' with current dword
        dec     ecx                     ;advance to next plane
        jns     vvcgb_plane4
        mov     [edi],edx               ;save dword
        add     edi,[incBitmap]
        dec     [nbScanCur]             ;have we finished off the scanline yet?
        jnz     vvcgb_scan4             ;no

vvcgb_ptradjust:
        mov     eax,dword ptr pPlane[ebx*4]
        sub     eax,[nbScan]            ;adjust for scanline just processed
        cmp     [yScaleCur],1
        ja      short vvcgb_ptrupdate
        sub     eax,[nbScanVirt]        ;adjust for next scanline
        cmp     [offOddEven],0          ;an even/odd bank mode?
        je      short vvcgb_ptrupdate   ;no
        test    [rowCur],1              ;did we just process an odd row?
        jz      short vvcgb_evenrow     ;no
        sub     eax,[offOddEven]        ;yes, get back up to the even bank
        add     eax,[nbScanVirt]
        jmp     short vvcgb_ptrupdate
vvcgb_evenrow:
        add     eax,[offOddEven]        ;bop down to the odd bank now
vvcgb_ptrupdate:
        mov     dword ptr pPlane[ebx*4],eax
        dec     ebx
        jns     vvcgb_ptradjust
        dec     [yScaleCur]
        jg      vvcgb_duprowloop
        dec     [rowCur]                ;decrement row # and check for more
        jmp     vvcgb_rowloop

vvcgb_exit:
        IFDEF SVGA                      ;                                          
        dec     iBankCur                ;                                          
        jnz     vvcgb_banksetup         ;                                          
        ENDIF                           ;                                          
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvCopyGraphicsToBitmap


;/***************************************************************************
;*
;* FUNCTION NAME = vvCopyGraphicsToDDBitmap()
;*
;* DESCRIPTION   = Copy graphics rectangle in DD-bitmap format
;*
;*
;* INPUT         = hvdm    -> VDM
;*                 pvvr    -> copy information
;*                 pBitmap -> destination bitmap buffer
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

       IFDEF EGAVGA
        align   4                                                       ;          
Procedure vvCopyGraphicsToDDBitmap
        ArgVar  hvdm,HVDM
        ArgVar  pvvr,PVVRECT
        ArgVar  pBitmap,PVOID

        LocalVar iPlane,ULONG           ;current plane #
        LocalVar iScan,ULONG            ;current scan #
        LocalVar iScanInit,ULONG        ;starting scan #
        LocalVar nScans,ULONG           ;# scans to transfer
        LocalVar nScansInit,ULONG
        LocalVar nScansPerSeg,ULONG     ;# scans per 64k segment

        LocalVar offTop,ULONG           ;beginning src offset for planes
        LocalVar offScan,ULONG          ;beginning dest offset for current plane
        LocalVar nbScan,ULONG           ;size of a scan line (one plane)
        LocalVar nbScanSet,ULONG        ;size of a scan line (set of planes)
        LocalVar nbScanDiff,ULONG       ;difference between logical and visual lines

        EnterProc
        SaveReg <ebx,esi,edi>

        mov     ebx,[hvdm]
        mov     esi,[pvvr]
        mov     esi,[esi].vvr_pbColorXlate
        call    vvAnalyzeColors         ;see if we can tweak the plane order

        mov     esi,[pvvr]
        mov     eax,[esi].vvr_rcl.yBottom
        sub     eax,[esi].vvr_rcl.yTop
        inc     eax
        mov     [nScansInit],eax
        or      eax,eax
        jz      vvcdd_exit              ;exit if no scanlines to copy

        mov     eax,VDMData.vvMode.vvm_nCols[ebx]
        mul     VDMData.nBitsPixel[ebx]
        shr     eax,3                   ;divide # bits by 8 to give # bytes
        mov     [nbScan],eax            ;# bytes per scan line
        movzx   eax,VDMData.aregCRTData[ebx+REG_CRTOFFSET]
        add     eax,eax
        sub     eax,[nbScan]
        mov     [nbScanDiff],eax        ;# bytes per logical scan line

        mov     eax,[nbScan]
        mov     ecx,VDMData.nPlanes[ebx]
        mul     ecx                     ;EAX == # bytes per scanline set
        sub     ecx,ecx
        mov     [iPlane],ecx
        mov     [offScan],ecx
        mov     [nbScanSet],eax

        test    VDMData.flVDMXVideo[ebx],VDMX_32DSP                     ;          
        jz      short vvcdd_doSegFixup                                  ;          

        mov     ecx,[esi].vvr_rcl.yTop                                  ;          
        mov     [iScanInit],ecx                                         ;          
        xchg    eax,ecx                                                 ;          
        cdq                                                             ;          
        mul     ecx                                                     ;          
        add     [pBitmap],eax                                           ;          
        jmp     short vvcdd_start                                       ;          

vvcdd_doSegFixup:                                                       ;          
        mov     ecx,10000h              ;ECX == 64k
        xchg    eax,ecx                 ;EAX == 64k, ECX == # bytes per set
        cdq
        div     ecx                     ;EAX == # sets per 64k
        mov     [nScansPerSeg],eax
        mov     ecx,[esi].vvr_rcl.yTop
        xchg    eax,ecx                 ;EAX == set #, ECX == # sets per 64k
        cdq
        div     ecx
        shl     eax,16                  ;multiply by 64k
        add     [pBitmap],eax           ;advance to correct 64k segment
        mov     [iScanInit],edx
        mov     eax,edx                 ;get remainder
        mul     [nbScanSet]
        add     [pBitmap],eax

vvcdd_start:                                                            ;          
        mov     eax,[nbScan]
        sub     [nbScanSet],eax
        mul     [esi].vvr_rcl.yTop
        mov     [offTop],eax            ;EAX == offset to first scan to copy

IFDEF   SVGA                                                            ;          
        cmp     [esi].vvr_rcl.xRight,1024-1                             ;          
        jae     vvcdd_1024x768                                          ;          
ENDIF                                                                   ;          

vvcdd_ptrsetup:
        mov     eax,[iScanInit]
        mov     [iScan],eax
        mov     eax,[nScansInit]
        mov     [nScans],eax
        mov     eax,[iPlane]
        movzx   eax,abPlaneOrder[ebx][eax]
        mov     edx,VDMData.anpgPlane[BANK0][ebx][eax*4]                ;          
        shl     edx,12                  ;convert # pages to # bytes
        mov     esi,VDMData.offPhysVRAMVisible[ebx]
        add     esi,[offTop]            ;advance to the top of req. rectangle
        mov     cl,al
        mov     ch,1
        shl     ch,cl                   ;CH == plane bit
        test    VDMData.aregATCData[ebx+REG_ATCPLANEENABLE],ch
        jz      short vvcdd_nobytes
;             test    VDMData.aregSEQData[ebx+REG_SEQMAPMASK],ch        ;j-ys0131temp!
;             jz      short vvcdd_nobytes                               ;j-ys0131temp!
        sub     edx,esi                 ;EDX == # valid bytes
        jnc     short vvcdd_validbytes
vvcdd_nobytes:
        sub     edx,edx                 ;there are *no* valid bytes
vvcdd_validbytes:
        add     esi,VDMData.apPlane[BANK0][ebx][eax*4]
        mov     edi,[pBitmap]
        add     edi,[offScan]

vvcdd_next_scan:
        mov     ecx,[nbScan]
        cmp     edx,ecx                 ;is there at least 1 scanline to copy?
        jb      short vvcdd_partial_scan
        sub     edx,ecx
        shr     ecx,2
        cmp     flInvertMask[ebx],0
        je      short vvcdd_justmove
        shr     ecx,1                                                   ;          
        align   4                                                       ;          
vvcdd_invmove:                                                          ;          
        lodsd                                                           ;          
        not     eax                                                     ;          
        stosd                                                           ;          
        lodsd                                                           ;          
        not     eax                                                     ;          
        stosd                                                           ;          
        hploop  vvcdd_invmove                                           ;          
        jmp     short vvcdd_scan_continue
vvcdd_justmove:
        rep     movsd                   ;transfer the scanline
vvcdd_scan_continue:
        add     edi,[nbScanSet]
        add     esi,[nbScanDiff]
        sub     edx,[nbScanDiff]
        jnc     short vvcdd_scan_inc
        sub     edx,edx
vvcdd_scan_inc:
        mov     eax,[iScan]
        inc     eax
        test    VDMData.flVDMXVideo[ebx],VDMX_32DSP                     ;          
        jnz     short vvcdd_seg_ok                                      ;          
        cmp     eax,[nScansPerSeg]
        jb      short vvcdd_seg_ok
        sub     eax,eax                 ;set scan index back to zero
        add     edi,10000h              ;kick destination pointer to next 64k
        and     edi,not 0FFFFh
        add     edi,[offScan]
vvcdd_seg_ok:
        mov     [iScan],eax

        dec     [nScans]
        jnz     short vvcdd_next_scan

        mov     eax,[nbScan]
        add     [offScan],eax
        mov     eax,[iPlane]
        inc     eax
        mov     [iPlane],eax
        cmp     eax,VDMData.nPlanes[ebx]
        jb      vvcdd_ptrsetup
        jmp     short vvcdd_exit

vvcdd_partial_scan:
        mov     ecx,edx                 ;finish off the real data
        cmp     flInvertMask[ebx],0
        je      short vvcdd_justfinish
        align   4                                                       ;          
vvcdd_invfinish:
        jecxz   short vvcdd_skiploop
        lodsb
        not     al
        stosb
        hploop  vvcdd_invfinish
vvcdd_skiploop:
        mov     eax,-1
        jmp     short vvcdd_fill
vvcdd_justfinish:
        rep     movsb
        sub     eax,eax

vvcdd_fill:
        mov     ecx,[nbScan]            ;then start filling in with zeros
        sub     ecx,edx
        sub     edx,edx
        push    ecx
        shr     ecx,2
        rep     stosd
        pop     ecx
        and     ecx,03h
        rep     stosb
        jmp     vvcdd_scan_continue

vvcdd_exit:
        RestoreReg <edi,esi,ebx>
        ExitProc

IFDEF   SVGA                                                            ;          

vvcdd_1024x768:

;/*
;**
;**             
;**         Special case handling of 1024x768 16-colour bitmap copy.
;**
;**   Note: The method below relies on a number of factors:
;**         o That a 64k segment contains an exact number of scanlines.
;**         o The requested copy size is ignored, the whole bitmap is
;**           always copied.
;**         o Number of pages per plane is always 16.
;**         o Offset to visible VRAM is always zero.
;**         o The inversion mask is ignored.
;**
;*/

vvcdd_nextplane:
        mov     eax,[iScanInit]
        mov     [iScan],eax
        mov     eax,[nScansInit]
        mov     [nScans],eax
        mov     eax,[iPlane]
        movzx   eax,abPlaneOrder[ebx][eax]
        mov     edx,10000H              ;64k
        mov     cl,al
        mov     ch,1
        shl     ch,cl                   ;CH == plane bit
        test    VDMData.aregATCData[ebx+REG_ATCPLANEENABLE],ch
        jnz     short @F
        sub     edx,edx                 ;there are *no* valid bytes
@@:
        mov     esi,VDMData.apPlane[ebx][eax*4]
        mov     edi,[pBitmap]
        add     edi,[offScan]

        align   4
vvcdd_next_1024_scan:
        mov     ecx,[nbScan]            ; 
        sub     edx,ecx                 ; 
        shr     ecx,2                   ; 
        rep     movsd                   ;transfer the scanline
        add     edi,[nbScanSet]         ; 
        mov     eax,[iScan]             ; 
        inc     eax                     ; 
        mov     [iScan],eax             ; 
        or      edx,edx                 ; 
        jz      vvcdd_next_partseg      ; 
vvcdd_dec_scan:
        dec     [nScans]                ;mark another one
        jnz     short vvcdd_next_1024_scan ;do another?

        mov     eax,[nbScan]            ;done the plane
        add     [offScan],eax           ; 
        mov     eax,[iPlane]            ; 
        inc     eax                     ; 
        mov     [iPlane],eax            ; 
        cmp     eax,VDMData.nPlanes[ebx];done them all?
        jb      vvcdd_nextplane         ;not yet
        jmp     vvcdd_exit              ;Exit

vvcdd_next_partseg:                     ;do next bank's portion
        mov     edx,8000H               ; 
        mov     eax,[iPlane]            ; 
        movzx   eax,abPlaneOrder[ebx][eax]
        add     al,4                    ;skip up to next bank
        mov     esi,VDMData.apPlane[ebx][eax*4]
        jmp     vvcdd_dec_scan          ; 
ENDIF                                                                   ;          

EndProc   vvCopyGraphicsToDDBitmap

       ENDIF ;EGAVGA


;/***************************************************************************
;*
;* FUNCTION NAME = vvAnalyzeColors()
;*
;* DESCRIPTION   = Determine if plane order can be improved
;*
;* INPUT         = EBX -> VDM
;*                 ESI -> PM color translation table
;*
;* OUTPUT        = abPlaneOrder may be modified, as well as abColorXlate if
;*                 the given table is different.
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

       IFDEF EGAVGA
        align   4                                                       ;          
Procedure vvAnalyzeColors
        LocalVar nMatches,ULONG
        LocalVar nEntries,ULONG
        LocalVar pBestEntry,ULONG
        LocalVar flInversion,ULONG

        EnterProc
        mov     ecx,16                  ;see if color table is new
        lea     edi,abColorXlate[ebx]
        align   4                                                       ;          
vvac_compare:
        lodsb
        and     al,0Fh                  ;only low 4 bits are interesting
        scasb                           ;same?
        loopz   vvac_compare            ;loop while they are
        jz      vvac_nochange
        inc     ecx
        dec     esi
        dec     edi
        align   4                                                       ;          
vvac_copy:
        lodsb
        and     al,0Fh
        stosb
        hploop  vvac_copy

        sub     edx,edx                 ;highest match so far (none)
        mov     flInversion,edx         ;initial inversion (none)
        mov     esi,FLAToffset abXlatTbl
        mov     pBestEntry,esi
vvac_retry:
        mov     nEntries,24             ;# of entries in abXlatTbl

vvac_checkentry:
        mov     nMatches,0
        mov     ecx,16                  ;now check all 16 bytes for this entry
        sub     edi,ecx                 ;back up to front of table again
        align   4                                                       ;          
vvac_checkloop:
        lodsb
        and     al,0Fh
        xor     al,byte ptr flInversion

;/*
;**
;**   This "approximation" code didn't seem to yield significantly better
;**   results, and in some cases made the images look worse than what we ended
;**   up with counting only exact matches, so it's disabled for now....
;**
;*/

       IFDEF APPROXIMATE
        sub     al,es:[edi]
        inc     edi
        cbw                             ;AH == FFh if negative, 00h if positive
        xor     al,ah
        sub     al,ah                   ;AL == ABS(AL)
        cmp     al,1                    ;are we within 1 of the color?
        ja      short vvac_different    ;no
       ELSE
        scasb
        jnz     short vvac_different
       ENDIF

        inc     nMatches
vvac_different:
        hploop  vvac_checkloop

        cmp     nMatches,edx            ;higher than previous high?
        jbe     short vvac_nextentry    ;no
        mov     edx,nMatches            ;yes, becomes new high
        lea     eax,[esi-16]
        mov     pBestEntry,eax
        mov     al,byte ptr flInversion ;record the inversion we used, if any
        mov     byte ptr flInversion+1,al
        cmp     edx,14                  ;are we maxed out? (OK, so 14 isn't
        jae     short vvac_done         ;the maximum, but's with PM's current
                                        ;settings, it's usually the best we do)
vvac_nextentry:
        dec     nEntries
        jnz     vvac_checkentry
        cmp     byte ptr flInversion,0  ;have we tried inversion yet?
        jne     short vvac_done         ;yes
        mov     byte ptr flInversion,0Fh;well, try it then
        mov     esi,FLAToffset abXlatTbl
        jmp     vvac_retry

vvac_done:
;             mov     ecx,4
        mov     esi,pBestEntry
        lea     edi,abPlaneOrder[ebx]
vvac_saveorder:
        rept    4                       ;4-peat in-line code            ;          
        lodsb
        shr     al,4                    ;high nibbles of 1st 4 bytes are order
        stosb
        endm                                                            ;          
        mov     al,byte ptr flInversion+1
        mov     byte ptr flInvertMask[ebx],al

vvac_nochange:
        ExitProc
EndProc   vvAnalyzeColors

       ENDIF ;EGAVGA


;/***************************************************************************
;*
;* FUNCTION NAME = vvConvertPalette()
;*
;* DESCRIPTION   = Copy VDM palette to RGB format
;*
;*                    FLAGS:
;*                       If bit 7 of REG_ATCMODECTRL is 0, bits 4 & 5 of DAC
;*                       input come from the ATC palette registers;  when bit 7 is 1,
;*                       bits 4 & 5 of DAC input are bits 0 & 1 of REG_ATCVGAREG.
;*                       And something altogether different may occur in 256-color
;*                       modes (I can't be sure just from reading the hardware spec).
;*                       So, for simplicity, I simply assume that on the VGA, the
;*                       palette table is a no-op (ie, that most apps treat it that
;*                       way), and always use the DAC to generate color info instead
;*
;*
;* INPUT         = hvdm -> VDM
;*                 pRGB -> storage for RGB data
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvConvertPalette
        ArgVar  hvdm,HVDM
        ArgVar  nColors,ULONG
        ArgVar  pRGB,PVOID

        EnterProc
        push    ebx
        push    edi

        mov     ebx,[hvdm]
        mov     edi,[pRGB]

        mov     ecx,1                   ;index adjustment
        sub     edx,edx                 ;current index
        cmp     [nColors],2
        jne     short vvcp_loop
        inc     edx
        neg     ecx                     ;monochrome bitmaps require reversal
        align   4                                                       ;          
vvcp_loop:
       IFDEF CGA
        movzx   eax,VDMData.regColor[ebx];FLAG: -- CGA 4-color
       ENDIF                            ;conversion needs to be handled

       IFDEF EGAVGA
        mov     eax,edx
        cmp     edx,16                  ;have we run out of ATC regs yet?
        jae     short vvcp_gotindex     ;yes
        movzx   eax,VDMData.aregATCData[ebx][edx]
vvcp_gotindex:
       ENDIF

       IFDEF CGAEGA
        and     al,3Fh                  ;make sure we only use 6 bits
        mov     eax,aulPalTbl[eax*4]    ;convert ATC data directly into RGB
        stosb
        shr     eax,8
        stosb
        shr     eax,8
        stosb
       ENDIF

       IFDEF VGA
        lea     eax,[eax+eax*2]         ;multiply by size of dac_s
        .ERRNZ  size dac_s NE 3
        mov     eax,dword ptr VDMData.adacDACData[ebx][eax]
        and     eax,3F3F3Fh             ;make sure we only use 6 bits each
        shl     eax,2                   ;x each color by 4 (8-bit conv.)
        ror     eax,16                  ;since DAC data is stored RGB and
        stosb                           ;we must store the data BGR, we need
        rol     eax,8                   ;to do a few simple rotations
        stosb
        rol     eax,8
        stosb
       ENDIF

        add     edx,ecx                 ;adjust current index
        dec     [nColors]
        jnz     vvcp_loop

        pop     edi
        pop     ebx
        ExitProc
EndProc   vvConvertPalette


;/***************************************************************************
;*
;* FUNCTION NAME = vvSetupShadow()
;*
;* DESCRIPTION   = Setup shadow buffer dimensions.
;*
;* INPUT         = EBX -> VDM
;*                 ESI -> input rectangle (if NULL, current size of shadow buffer used)
;*                 EDI -> output rectangle (cannot be NULL)
;*
;* OUTPUT        = ECX == # bytes per scanline
;*                 EDX == starting buffer offset
;*                 ESI == largest valid buffer offset
;*                 Carry clear if valid rectangle, set if not
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvSetupShadow
        mov     [edi].xLeft,0
        mov     eax,VDMData.vvMode.vvm_nCols[ebx]
        dec     eax
        mov     [edi].xRight,eax
        inc     eax
        mul     VDMData.nBitsPixel[ebx]
        shr     eax,3                   ;divide # bits by 8 to give # bytes
        mov     ecx,eax                 ;# bytes per scan line
        or      esi,esi
        jz      short vvsetup_default
        mov     eax,[esi].yTop
        mov     [edi].yTop,eax
        mov     eax,[esi].yBottom       ;set up specified rectangle
        mov     [edi].yBottom,eax
        jmp     short vvsetup_compute

vvsetup_default:
        mov     [edi].yTop,0
        mov     eax,VDMData.vvMode.vvm_nRows[ebx]
        dec     eax
        mov     [edi].yBottom,eax       ;set up default rectangle

vvsetup_compute:

;/*
;**
;**   Determine the last row than can be copied, given the current
;**   size of the shadow buffer;  the only thing that really complicates
;**   this is handling of the banked CGA modes....
;**
;*/

        mov     eax,VDMData.npgShadowPlane[ebx]
        shl     eax,12                  ;EAX == # bytes in shadow buffer
        push    eax
        sub     eax,ecx                 ;subtract one row's worth

        sub     esi,esi                 ;now figure out max. row #
        mov     edx,VDMData.offOddBank[ebx]
        cmp     eax,edx                 ;smaller than bank offset?
        jb      short vvsetup_fixbottom ;yes, that's bad
        sub     eax,edx                 ;subtract bank offset
        push    edx
        cdq
        div     ecx                     ;EAX == corresponding max. row #
        pop     edx
        push    ecx
        sub     ecx,ecx                 ;ECX == 0
        cmp     edx,1
        cmc                             ;set carry if EDX non-zero
        adc     ecx,0                   ;ECX == 1 if EDX non-zero
        shl     eax,cl
        add     eax,ecx                 ;max. row # is now "bank-adjusted"
        pop     ecx
        mov     esi,eax                 ;ESI == max. row #

vvsetup_fixbottom:
        cmp     [edi].yBottom,esi       ;is bottom of rectangle valid?
        jbe     short vvsetup_bottomvalid
        mov     [edi].yBottom,esi       ;no, so fix it

vvsetup_bottomvalid:
        mov     eax,[edi].yTop
        cmp     [edi].yBottom,eax       ;is bottom still greater than top?
        jae     short vvsetup_topvalid  ;yes
        mov     eax,[edi].yBottom       ;no, so we have to fix the top
        mov     [edi].yTop,eax

vvsetup_topvalid:
        pop     esi                     ;ESI == # bytes in shadow buffer

;/*
;**
;**   Now to determine the starting offset for specified first row;
;**   again, the only thing that really complicates this are the banked
;**   CGA modes....
;**
;*/

        or      edx,edx                 ;bank offset?
        jz      short vvsetup_rowvalid  ;no
        shr     eax,1                   ;yes, so cut row in half
        jc      short vvsetup_rowvalid
        sub     edx,edx                 ;for even row, drop bank offset

vvsetup_rowvalid:
        imul    eax,ecx                 ;get offset of first row
        add     eax,VDMData.offPhysVRAMVisible[ebx]
        add     edx,eax                 ;add row offset to the bank offset
        cmp     edx,esi                 ;offset too large?

IFDEF   VGA                                                             ;          
        jnc     @F                      ;bail out if already in error   ;          
        mov     eax,[edi].yBottom       ;Check the rectangle will fit   ;          
        sub     eax,[edi].yTop          ; into available shadow buffer  ;          
        imul    eax,ecx                 ;how many bytes do we need      ;          
        add     eax,ecx                 ;subtraction isn't inclusive    ;          
        add     eax,VDMData.offPhysVRAMVisible[ebx]                     ;          
        cmp     eax,esi                 ;end offset too large?          ;          
@@:                                                                     ;          
ENDIF                                                                   ;          
        cmc
        ExitProc
EndProc   vvSetupShadow


;/***************************************************************************
;*
;* FUNCTION NAME = vvFillShadowBuffer()
;*
;* DESCRIPTION   = Fill shadow buffer with data
;*
;* INPUT         = hvdm -> VDM
;*                 prcl -> rectangle describing scan lines to fill (or NULL for all)
;*
;* OUTPUT        = SUCCESS
;*                     TRUE
;*                 FAILURE
;*                     FALSE (no shadow memory allocated)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvFillShadowBuffer
        ArgVar  hvdm,HVDM
        ArgVar  prcl,PRECTL

        LocalVar rectl,RECTL

        EnterProc
        push    ebx
        push    esi
        push    edi

        mov     ebx,[hvdm]
        mov     esi,[prcl]
        SSToDS  edi,[rectl]
        call    vvSetupShadow
        jb      short vvfill_exit       ;no rectangle to fill

;/*
;**
;**     ECX == # bytes per scanline
;**     EDX == starting buffer offset
;**     [rectl] describes rectangle to fill
;**
;*/

        mov     edi,VDMData.pShadowPlane[ebx]
        add     edi,edx
        mov     esi,VDMData.apPlane[BANK0][PLANE0][ebx]                 ;          
        add     esi,edx
        mov     edx,[rectl].yTop        ;EDX == line #
        align   4                                                       ;          
vvfill_loop:
        cmp     edx,[rectl].yBottom     ;reached bottom yet?
        jae     short vvfill_exit       ;yes                            ;          
;;      ja      short vvfill_exit       ;yes

        mov     eax,ecx                 ;save scan line length          ;          
        and     ecx,03h                 ;                               ;          
        jz      short vvfill_movsd      ;odd bytes?                     ;          
        rep     movsb                   ;yes, take care of them now!    ;          
vvfill_movsd:                           ;                               ;          
        mov     ecx,eax                 ;recall scan line length        ;          
        shr     ecx,2                   ;dword align it                 ;          
        jz      short vvfill_skipmovsd  ;anything to move?              ;          
;;      jecxz   short vvfill_skipmovsd  ;anything to move?              ;          
        rep     movsd                   ;fill one scan line             ;          
vvfill_skipmovsd:                       ;                               ;          
        mov     ecx,eax                 ;EDI and ESI -> next scan line  ;          

        inc     edx                     ;advance line #
        mov     eax,VDMData.offOddBank[ebx]
        or      eax,eax                 ;banked memory?
        jz      vvfill_loop             ;no
        test    edx,1                   ;odd row?
        jz      short vvfill_evenrow    ;no
        sub     esi,ecx                 ;yes, so subtract line adjustment
        sub     edi,ecx
        add     esi,eax                 ;and add in bank adjustment
        add     edi,eax
        jmp     vvfill_loop
vvfill_evenrow:
        sub     esi,eax                 ;adjust pointers back to even bank
        sub     edi,eax
        jmp     vvfill_loop

vvfill_exit:
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvFillShadowBuffer


;/***************************************************************************
;*
;* FUNCTION NAME = vvDiffShadowBuffer()
;*
;* DESCRIPTION   = Find differences in buffers
;*
;* INPUT         = hvdm -> VDM
;*                 prcl -> rectangle to return with scan line changes
;*
;* OUTPUT        = SUCCESS
;*                     TRUE
;*                 FAILURE
;*                     FALSE (no differences)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvDiffShadowBuffer
        ArgVar  hvdm,HVDM
        ArgVar  prcl,PRECTL

        LocalVar offOddEven,ULONG
        LocalVar yBottomOrig,ULONG

        EnterProc
        push    ebx
        push    esi
        push    edi
        mov     ebx,[hvdm]
        mov     esi,[prcl]
        mov     edi,esi
        call    vvSetupShadow
        jb      short vvdiff_exit       ;no rectangle to diff

;/*
;**
;**     ECX == # bytes per scanline
;**     EDX == starting buffer offset
;**     [rectl] describes rectangle to diff
;**
;*/

        mov     eax,VDMData.offOddBank[ebx]
        mov     [offOddEven],eax
        mov     edi,VDMData.pShadowPlane[ebx]
        add     edi,edx
        mov     esi,VDMData.apPlane[BANK0][PLANE0][ebx]                 ;          
        add     esi,edx
        mov     ebx,[prcl]
        mov     edx,[ebx].yTop          ;EDX == line #
        mov     eax,[ebx].yBottom
        mov     [yBottomOrig],eax
        align   4                                                       ;          
vvdiff_loop:
        cmp     dx,word ptr [yBottomOrig]
        jae     short vvdiff_exit       ;reached bottom                 ;          
;;      ja      short vvdiff_exit       ;reached bottom


        push    ecx
        repe    cmpsb                   ;compare one scan line
        jz      short vvdiff_nextscan   ;no differences
        or      edx,edx                 ;any previous differences?
        js      short vvdiff_setbottom  ;yes
        mov     [ebx].yTop,edx          ;no, so record first different row
        or      edx,80000000h

vvdiff_setbottom:
        mov     word ptr [ebx].yBottom,dx

vvdiff_nextscan:
        inc     edx                     ;advance line #
        add     esi,ecx
        add     edi,ecx
        pop     ecx
        mov     eax,[offOddEven]
        or      eax,eax                 ;banked memory?
        jz      vvdiff_loop             ;no
        test    edx,1                   ;odd row?
        jz      short vvdiff_evenrow    ;no
        sub     esi,ecx                 ;yes, so subtract line adjustment
        sub     edi,ecx
        add     esi,eax                 ;and add in bank adjustment
        add     edi,eax
        jmp     vvdiff_loop
vvdiff_evenrow:
        sub     esi,eax                 ;adjust pointers back to even bank
        sub     edi,eax
        jmp     vvdiff_loop

vvdiff_exit:
        mov     eax,0
        jb      short vvdiff_leave
        shl     edx,1
        adc     eax,0                   ;EAX == TRUE if EDX had high bit set
vvdiff_leave:
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvDiffShadowBuffer


;/***************************************************************************
;*
;* FUNCTION NAME = vvDiffPhysPage()
;*
;* DESCRIPTION   = Diff-and-copy page of physical memory (all planes)
;*
;*  For pages currently residing in physical memory, this routine diffs
;*  the physical memory contents against the VDM's virtual video memory page
;*  and returns the smallest difference that encompasses all changes.  This
;*  change is recorded if fFlush is FALSE (ie, when called to      the
;*  page out of physical memory).  When fFlush is TRUE (ie, when called to
;*  actually report a difference), the recorded data is zeroed.
;*
;* INPUT         = hvdm     -> VDM
;*                 iPage    == index of page to diff-and-copy
;*                 poffDIff -> where to return offset into this page of difference
;*                 pnbDIff  -> where to return size of difference
;*                 fFlush   == TRUE to flush difference, FALSE to preserve
;*
;* OUTPUT        = Differences
;*                     TRUE
;*                 No differences
;*                     FALSE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

       IFDEF EGAVGA
        align   4                                                       ;          
Procedure vvDiffPhysPage
        ArgVar  hvdm,HVDM
        ArgVar  iPage,ULONG
        ArgVar  poffDiff,PULONG
        ArgVar  pnbDiff,PULONG
        ArgVar  fFlush,BOOL

        LocalVar iPlane,ULONG
        LocalVar pbPhys,PBYTE
        LocalVar pPlane,PBYTE
        LocalVar offTopDiff,ULONG
        LocalVar offBottomDiff,ULONG

        EnterProc
        push    ebx
        push    esi
        push    edi
        mov     ebx,[hvdm]

        mov     ecx,[iPage]
        mov     edx,VDMData.aoffPhysDiff[ebx][ecx*4]
        mov     [offTopDiff],edx
        mov     eax,VDMData.anbPhysDiff[ebx][ecx*4]
        mov     [offBottomDiff],eax
        or      eax,eax                 ;any difference?
        jz      short vvphys_ready
        add     eax,edx
        mov     [offBottomDiff],eax

;/*
;**
;**   EAX == offBottomDiff and ECX == iPage
;**   (the jump to vvphys_getdiff depends on that!)
;**
;*/

vvphys_ready:
        mov     edx,VDMData.aiVirtToPhys[ebx][ecx*4]
        or      edx,edx
        jz      vvphys_getdiff          ;page not mapped, use pre-existing data

        .ERRNZ  PAGESIZE NE 4096
        shl     edx,12                  ;EDX == iPage*PAGESIZE
        add     edx,[pPhysVRAM]
        mov     [pbPhys],edx            ;pbPhys -> physical page
        mov     [iPlane],PLANE3
        align   4                                                       ;          
vvphys_planeloop:
        CallFn  vvSetCopyState,<ebx, MEMORY_GRFX, iPlane>

;/*
;**
;**   Do comparison from top down
;**
;*/

        mov     esi,[iPlane]
        mov     esi,VDMData.apPlane[BANK0][ebx][esi*4]                  ;          
        mov     eax,[iPage]
        .ERRNZ  PAGESIZE NE 4096
        shl     eax,12                  ;EAX == iPage*PAGESIZE
        add     esi,eax
        mov     [pPlane],esi
        mov     edi,[pbPhys]
        mov     ecx,PAGESIZE/4
        repe    cmpsd                   ;do dword compare
        jz      short vvphys_nodiff     ;no differences
        sub     esi,4
        sub     esi,[pPlane]            ;ESI == offset of first difference
        mov     edx,esi                 ;EDX == offset for this plane
        cmp     esi,[offTopDiff]        ;does it encompass any previous offset?
        jae     short vvphys_diffbottom
        mov     [offTopDiff],esi        ;yes, save it

;/*
;**
;**   Now do the comparison from the bottom up
;**
;*/

vvphys_diffbottom:
        mov     esi,[pPlane]
        add     esi,PAGESIZE-4
        mov     edi,[pbPhys]
        add     edi,PAGESIZE-4
        mov     ecx,PAGESIZE/4
        std
        repe    cmpsd
        cld
        add     esi,8
        sub     esi,[pPlane]            ;ESI == offset of last difference
        cmp     esi,[offBottomDiff]     ;does it encompass any previous offset?
        jbe     short vvphys_copy       ;no
        mov     [offBottomDiff],esi     ;yes, save it

vvphys_copy:
        mov     ecx,esi                 ;copy only what was different
        sub     ecx,edx
       IFDEF VDDSTRICT
        test    ecx,03h                 ;make sure count is a dword multiple
        ASSERTZ <"Bad diff count",10>,<00>
       ENDIF
        shr     ecx,2                   ;convert to # dwords
        mov     esi,[pbPhys]
        add     esi,edx
        mov     edi,[pPlane]
        add     edi,edx
        rep     movsd                   ;update shadow data

vvphys_nodiff:
        dec     iPlane
        jns     vvphys_planeloop

        mov     ecx,[iPage]
        mov     eax,[offBottomDiff]     ;any difference?
vvphys_getdiff:
        or      eax,eax
        jz      short vvphys_exit       ;no, EAX == FALSE return code, too

        mov     edx,[offTopDiff]        ;return values to caller
        mov     edi,[poffDiff]
        or      edi,edi
        jz      short vvphys_nextptr
        mov     [edi],edx
vvphys_nextptr:
        mov     VDMData.aoffPhysDiff[ebx][ecx*4],edx
        sub     eax,edx                 ;compute size of difference
        ASSERTNC <"vvDiffPhysPage: bad diff",10>,<00>
        mov     edi,[pnbDiff]
        or      edi,edi
        jz      short vvphys_nextptr2
        mov     [edi],eax
vvphys_nextptr2:
        mov     VDMData.anbPhysDiff[ebx][ecx*4],eax

vvphys_exit:
        cmp     fFlush,FALSE            ;flushing?
        je      short vvphys_getout     ;no
        mov     VDMData.aoffPhysDiff[ebx][ecx*4],PAGESIZE
        mov     VDMData.anbPhysDiff[ebx][ecx*4],0

vvphys_getout:
        pop     edi
        pop     esi
        pop     ebx
        ExitProc
EndProc   vvDiffPhysPage

       ENDIF ;EGAVGA


        EndCode     PRIVATE,SWAP,PASCAL


        END
