;*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    VVPTRA
        TITLE   Virtual Video Low-level Pointer Routines

;/*****************************************************************************
;*
;* SOURCE FILE NAME = VVPTRA.ASM
;*
;* DESCRIPTIVE NAME = Virtual Video Low-level Pointer Routines
;*
;*
;* VERSION      V2.0
;*
;* DATE         11/10/88
;*
;* DESCRIPTION  This module contains the VVD's pointer drawing/erasing code.
;*
;* FUNCTIONS
;*              vvPtrReset()                 Update pointer data according to new video mode
;*              vvPtrSetMasks()              Copy and prep new pointer masks.
;*              vvPtrCheckInverse            Determines inverseness of pointer.
;*              vvPtrMoveMask                Copies and preps a single mask array
;*              vvPtrDraw()                  Draw mouse pointer
;*              vvPtrErase()                 Erase mouse pointer.
;*              vvPtrRestore()               Restore screen under mouse pointer.
;*              vvPtrGetWord()               Read a word of VRAM during retrace.
;*              vvPtrPutByte()               Write a byte of VRAM during retrace
;*              vvPtrPutWord()               Write a word of VRAM during retrace.
;*              vvPtrWaitRetrace()           Wait for retrace interval
;*              vvPtrDrawGraph               Draws pointers for graphics modes.
;*              vvPtrEraseGraph              Erases pointers for graphics modes
;*              vvPtrEraseOldPointer         Erase Old Pointer
;*              vvPtrRotateMasks             Rotate masks.
;*              vvPtrRotateRight             Rotate right
;*              vvPtrRotateLeft              Rotate left
;*              vvPtrPutPointerInScrSave     Current pointer is ANDed and XORed into the
;*                                           current local buffer.
;*              vvPtrPutPointerInScrSave1    Worker for vvPtrPutPointerInScrSave
;*              vvPtrPutNormal               Put the pointer into the screen buffer
;*                                           (used for all display modes )
;*              vvPtrPutEGA                  Just like vvPtrPutNormal, but used for EGA
;*                                           operation when using
;*              vvPtrPutVGA                  Just like vvPtrPutNormal, but is used for VGA
;*                                           operation when using
;*              vvPtrCopyPtrSaveToScreen     Restore the contents of the old screen to
;*              vvPtrCopyPtrSaveToScreen1    Worker for vvPtrCopyPtrSaveToScreen
;*              vvPtrCopyPtrSaveToScrSave    Restore the contents of the old screen to
;*              vvPtrCopyPtrSaveToScrSave1   Worker for vvPtrCopyPtrSaveToScrSave
;*              vvPtrCopyScrSaveToScreen     The contents of the screen buffer is copied
;*                                           to the screen.
;*              vvPtrCopyScrSaveToScreen1    Worker for vvPtrCopyScrSaveToScreen
;*              vvPtrCopyScrSaveToPtrSave    Worker for vvPtrCopyScrSaveToScreen
;*              vvPtrCopyScrSaveToPtrSave1   Worker for vvPtrCopyScrSaveToPtrSave
;*              vvPtrCopyScreenToScrSave     Copy the contents of the given region
;*                                           of the screen
;*              vvPtrCopyScreenToScrSave1    Worker for vvPtrCopyScreenToScrSave.
;*              vvPtrCopyBufferToScreen      Copy contents of the pointer-save buffer
;*                                           or screen-save buffer
;*              vvPtrGetOffsetIntoScrSave    The given screen (x,y) coordinate is mapped
;*                                           into a pointer
;*              vvPtrGetScrBuffOffset        Get screen buffer offset
;*              vvPtrGetScreenOffset         Compute screen address of point (EAX,ESI)
;*              vvPtrEnableReadMap           Enable read map
;*              vvPtrEnableWriteMaps         Enable write maps
;*              vvPtrSetState                Save (if necessary) and set video stat
;*              vvPtrRestoreState            Restore video state if it was changed
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        CHANGE DESCRIPTION
;*   --------  ----------  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxxxx
;*
;*   11/10/88              Created.
;*
;*   07/31/91              B777678  Don't double grid for 640 pel mode (CGA)
;*
;*   01/16/92              B731231  Fix video page calculation
;*
;*   02/12/92              B732715  CodeView doesn't use INT 33h to
;*                                  register CRT page usage
;*
;*   02/15/92              B733006  Performance enhancement to copy routines
;*
;*   02/27/92              B733211  Performance enhancement to 320x200 mode
;*
;*   03/05/92              B726713             fix
;*   07/01/94               88650   Update shadow register for determining packed mode
;*
;*****************************************************************************/


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


        DefData     IMPORT,SWAP,C
        PBYTE       pPhysVRAM
        ULONG       npgPlane
       IFDEF EGAVGA
        PPRLE       aprlePtrState
        RLE         rlePtrSEQMapMask
        RLE         rlePtrGDCMode
       ENDIF
        EndData


        DefCode     IMPORT,SWAP,PASCAL
       IFDEF VGA
        DefFn       vvGetIOState
       ENDIF
       IFDEF EGAVGA
        DefFn       vvSetIOState
        DefFn       vvSaveLatches
        DefFn       vvRestoreLatches
       ENDIF    ;EGAVGA
        EndCode


        DefCode     PRIVATE,SWAP,PASCAL

        DefFn       vvPtrReset
        DefFn       vvPtrSetMasks       ;called from vvptr.c
        DefFn       vvPtrCheckInverse
        DefFn       vvPtrMoveMask
        DefFn       vvPtrMoveNormal
        DefFn       vvPtrMoveEGA
        DefFn       vvPtrDraw           ;called from vvptr.c
        DefFn       vvPtrErase          ;called from vvptr.c
        DefFn       vvPtrRestore
        DefFn       vvPtrDrawGraph
        DefFn       vvPtrEraseGraph
        DefFn       vvPtrEraseOldPointer
        DefFn       vvPtrRotateMasks
        DefFn       vvPtrRotateRight
        DefFn       vvPtrRotateLeft
        DefFn       vvPtrPutPointerInScrSave
        DefFn       vvPtrPutPointerInScrSave1
        DefFn       vvPtrPutNormal
        DefFn       vvPtrPutEGA
        DefFn       vvPtrPutVGA
        DefFn       vvPtrCopyPtrSaveToScreen
        DefFn       vvPtrCopyPtrSaveToScreen1
        DefFn       vvPtrCopyPtrSaveToScrSave
        DefFn       vvPtrCopyPtrSaveToScrSave1
        DefFn       vvPtrCopyScrSaveToScreen
        DefFn       vvPtrCopyScrSaveToScreen1
        DefFn       vvPtrCopyScrSaveToPtrSave
        DefFn       vvPtrCopyScrSaveToPtrSave1
        DefFn       vvPtrCopyScreenToScrSave
        DefFn       vvPtrCopyScreenToScrSave1
        DefFn       vvPtrCopyBufferToScreen
        DefFn       vvPtrGetOffsetIntoScrSave
        DefFn       vvPtrGetScrBuffOffset
        DefFn       vvPtrGetScreenOffset
        DefFn       vvPtrEnableReadMap
        DefFn       vvPtrEnableWriteMaps
       IFDEF EGAVGA
        DefFn       vvPtrSetState
        DefFn       vvPtrRestoreState
       ENDIF


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrReset()
;*
;* DESCRIPTION   = Update pointer data according to new video mode
;*
;* INPUT         = hvdm -> VDM
;*                 vmssVideo video mode data filled in
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrReset
        ArgVar  hvdm,HVDM

        EnterProc
        SaveReg <ebx>
        mov     ebx,[hvdm]              ;EBX -> VDM data
        add     ebx,FLAToffset VDMData

;/*
;**
;**   Reset flags and assume a text mode posture
;**
;*/

        and     [ebx].PtrData.flPtrVideo,PTR_RESETMASK
        mov     eax,[ebx].PtrData.vmssVideo.vmss_ulCellWidth
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrWidth,eax
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrUnitWidth,eax
        mov     eax,[ebx].PtrData.vmssVideo.vmss_ulCellHeight
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrHeight,eax
        mov     [ebx].PtrData.iPtrMode,PTRMODE_NOPLANE
        mov     [ebx].PtrData.pfnMoveMasks,0

;/*
;**
;**   Set up off-screen buffer addresses
;**
;*/

        mov     eax,[ebx].nbPhysVRAMVisible
        add     eax,SIZRESERVED         ;get offset to ptrsave area
        mov     [ebx].PtrData.offPtrSave,eax
        add     eax,PTRSAVSIZE          ;get offset to scrsave area
        mov     [ebx].PtrData.offScrSave,eax

        mov     edx,[ebx].PtrData.vmssVideo.vmss_ulCellWidth
        or      edx,[ebx].PtrData.vmssVideo.vmss_ulCellHeight
        cmp     dl,1
        ja      reset9                  ;a text mode, so we're done

;/*
;**
;**   Start with simple graphics mode assumptions
;**
;*/

        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrWidth,SCRSAVWD*8
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrHeight,SCRSAVHT
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrUnitWidth,8
        mov     [ebx].PtrData.pfnPutImage,FLAToffset vvPtrPutNormal
        mov     [ebx].PtrData.pfnMoveMasks,FLAToffset vvPtrMoveNormal

        cmp     [ebx].offOddBank,0
        je      short reset0            ;not a CGA mode, so continue
        cmp     [ebx].PtrData.vmssVideo.vmss_ulWidth,320
        jne     reset9                  ;CGA 640x200 mode is all set up
        shr     [ebx].PtrData.vmssVideo.vmss_ulPtrWidth,1
        shr     [ebx].PtrData.vmssVideo.vmss_ulPtrUnitWidth,1
        jmp     reset9                  ;we're done with all the CGA modes now

;/*
;**
;**   Change the assumptions to 4-plane graphic modes
;**
;*/

reset0:

       IFDEF   EGAVGA
        mov     [ebx].PtrData.iPtrMode,PTRMODE_4PLANE
        mov     [ebx].PtrData.pfnPutImage,FLAToffset vvPtrPutEGA
        mov     [ebx].PtrData.pfnMoveMasks,FLAToffset vvPtrMoveEGA

        cmp     [ebx].nBitsPixel,8
        jb      short reset1            ;not 256-color mode
        or      [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        add     [ebx].PtrData.offScrSave,BYTEPTRSAVSIZE-PTRSAVSIZE
        mov     [ebx].PtrData.iPtrMode,PTRMODE_NOPLANE
        mov     [ebx].PtrData.pfnPutImage,FLAToffset vvPtrPutVGA
        mov     [ebx].PtrData.vmssVideo.vmss_ulPtrUnitWidth,1
resetx: jmp     short reset9            ;256-color setup complete

reset1: test    [ebx].aregSEQData[REG_SEQMEMMODE],SEQMEM_ALL
        jnz     short reset2            ;not 64k EGA graphics mode
        or      [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        mov     [ebx].PtrData.iPtrMode,PTRMODE_ODDPLANE
        jmp     short reset9            ;64k EGA graphics mode setup complete

reset2: cmp     [ebx].aregSEQData[REG_SEQMAPMASK],SEQMAP_PLANE0
        jne     short reset9            ;not a single-plane graphics mode
        mov     [ebx].PtrData.iPtrMode,PTRMODE_1PLANE
       ENDIF ;EGAVGA

reset9: or      [ebx].PtrData.flPtrVideo,PTR_SUPPORTED

        RestoreReg <ebx>
        ExitProc
EndProc   vvPtrReset


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrSetMasks()
;*
;* DESCRIPTION   = Copy and prep new pointer masks.
;*
;* INPUT         = hvdm   -> VDM
;*                 pMasks -> new screen and pointer masks
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrSetMasks
        ArgVar  hvdm,HVDM
        ArgVar  pMasks,PUSHORT

        EnterProc
        SaveReg <ebx,esi,edi>

        mov     ebx,[hvdm]              ;EBX -> VDM data
        add     ebx,FLAToffset VDMData

        mov     esi,[pMasks]            ;set source address
        call    vvPtrCheckInverse       ;determine if pointer is inverse

        mov     edx,-1
        lea     edi,[ebx].PtrData.abANDMask     ;set destination address
        call    vvPtrMoveMask           ;move and prep the AND mask

        sub     edx,edx
        mov     [ebx].PtrData.nBitsRotate,edx
        lea     edi,[ebx].PtrData.abXORMask     ;set destination address
        call    vvPtrMoveMask           ;and move and prep the XOR mask

        RestoreReg <edi,esi,ebx>
        ExitProc
EndProc   vvPtrSetMasks


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCheckInverse
;*
;* DESCRIPTION   = Determines inverseness of pointer.
;*
;* INPUT         = EBX -> VDM instance data
;*                 ESI -> masks
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCheckInverse
        and     [ebx].PtrData.flPtrVideo,NOT PTR_INVERSE
        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jnz     short inverse_exit

        SaveReg <esi,edi>
        mov     eax,MAX_PTRWIDTH*MAX_PTRHEIGHT
        mov     ecx,eax
        mov     edi,eax

        or      [ebx].PtrData.flPtrVideo,PTR_INVERSE
        align   4                                                       ;          
inverse_loop:
        mov     al,[esi]
        and     al,[esi+edi]
        jnz     short inverse_done
        inc     esi
        hploop  inverse_loop
        and     [ebx].PtrData.flPtrVideo,NOT PTR_INVERSE

inverse_done:
        RestoreReg <edi,esi>

inverse_exit:
        ExitProc
EndProc   vvPtrCheckInverse


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrMoveMask
;*
;* DESCRIPTION   = Copies and preps a single mask array
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDX -> mask-type extension
;*                 ESI -> mask to copy and prep
;*                 EDI -> prepped mask destination
;*
;* OUTPUT        = ESI -> past current mask (ie, start of next mask, if any)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrMoveMask
        cmp     [ebx].PtrData.pfnMoveMasks,0
        je      short movemask_exit

        mov     ecx,PTRSAVHT
        test    [ebx].PtrData.flPtrVideo,PTR_INVERSE
        jnz     short vvPtrMoveNormal
        jmp     [ebx].PtrData.pfnMoveMasks
        align   4                                                       ;          
Entry vvPtrMoveNormal
        REPT    PTRSAVWD/2
        lodsw
        xchg    al,ah
        stosw
        ENDM
        IF      PTRSAVWD AND 1
        mov     al,dl
        stosb
        ENDIF
        hploop  vvPtrMoveNormal
        jmp     short movemask_exit
        align   4                                                       ;          
Entry vvPtrMoveEGA
        REPT    PTRSAVWD/2
        lodsw
        xchg    al,ah
        xor     eax,edx
        stosw
        ENDM
        IF      PTRSAVWD AND 1
        xor     al,al
        stosb
        ENDIF
        hploop  vvPtrMoveEGA

movemask_exit:
        ExitProc
EndProc   vvPtrMoveMask


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrDraw()
;*
;* DESCRIPTION   = Draw mouse pointer
;*
;* INPUT         = hvdm -> VDM
;*                 xNew == new x-coordinate (always pixels, never cells)
;*                 yNew == new y-coordinate (always pixels, never cells)
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrDraw
        ArgVar  hvdm,HVDM
        ArgVar  xNew,ULONG
        ArgVar  yNew,ULONG

        EnterProc
        SaveReg <ebx,esi,edi>
        mov     ebx,[hvdm]              ;EBX -> VDM
        movzx   edx,VDMBase.rb_wVLen[ebx]
        movzx   eax,VDMBase.rb_bVPage[ebx] ;use BIOS CRT page           ;          
        add     ebx,FLAToffset VDMData  ;EBX -> VDM data

        test    [ebx].PtrData.flPtrVideo,PTR_SUPPORTED
        jz      draw_exit

        mov     edi,[pPhysVRAM]         ;EDI -> VRAM (thru uvirt alias)
        add     edi,[ebx].offPhysVRAMActive
        mov     esi,edi                 ;ESI == scratch copy

        mul     edx                     ;BIOS defines size of video page
        add     esi,eax                 ;adjust ESI accordingly
        add     esi,[ebx].nbPhysVRAMVisible
        sub     esi,[pPhysVRAM]         ;ESI == difference
        mov     ecx,[npgPlane]
        shl     ecx,10                  ;ECX == # bytes in VRAM alias
        add     ecx,[ebx].offPhysVRAMActive                             ;        
        cmp     esi,ecx                 ;is ESI too large?
        jbe     short draw_pageok       ;no
        sub     eax,eax                 ;force video page offset to zero

draw_pageok:
        add     edi,eax                 ;EDI is now adjusted for video page

        mov     edx,[ebx].PtrData.vmssVideo.vmss_ulCellWidth
        or      edx,[ebx].PtrData.vmssVideo.vmss_ulCellHeight

;/*
;**
;**   Validate the pointer coordinates
;**
;*/

       IFDEF VDDSTRICT
        mov     eax,[xNew]
        cmp     eax,[ebx].PtrData.vmssVideo.vmss_ulWidth
        jb      short draw_xok
        ASSERT  <"Bad x-coordinate (%04x)",10>,eax
        mov     eax,[ebx].PtrData.vmssVideo.vmss_ulWidth
        dec     eax
        mov     [xNew],eax
       draw_xok:
       ENDIF
       IFDEF VDDSTRICT
        mov     eax,[yNew]
        cmp     eax,[ebx].PtrData.vmssVideo.vmss_ulHeight
        jb      short draw_yok
        ASSERT  <"Bad y-coordinate (%04x)",10>,eax
        mov     eax,[ebx].PtrData.vmssVideo.vmss_ulHeight
        dec     eax
        mov     [yNew],eax
       draw_yok:
       ENDIF

;/*
;**
;**   Make coordinates mode-dependent (cell-based or pixel-based)
;**
;*/

        cmp     dl,1                    ;graphics mode?
        jbe     short draw_checkmove    ;yes, leave pixel-based

        push    edx
        mov     eax,[xNew]              ;convert to cell-based
        cdq
        div     [ebx].PtrData.vmssVideo.vmss_ulCellWidth
        mov     [xNew],eax              ;save x, in cell units now

        mov     eax,[yNew]
        cdq
        div     [ebx].PtrData.vmssVideo.vmss_ulCellHeight
        mov     [yNew],eax              ;save y, in cell units now
        pop     edx

;/*
;**
;**   If pointer is currently displayed, determine if motion occurred
;**
;*/

draw_checkmove:
        test    [ebx].PtrData.flPtrVideo,PTR_DRAWN   ;pointer already up?
        jz      short draw_ready        ;no

        mov     eax,[xNew]
        cmp     eax,[ebx].PtrData.xCell ;change in x?
        jne     short draw_restore      ;yes

        mov     eax,[yNew]
        cmp     eax,[ebx].PtrData.yCell ;change in y?
        je      draw_exit               ;no, just exit

draw_restore:
        cmp     dl,1                    ;graphics mode?
        jbe     short draw_ready        ;yes, restore done automatically
        call    vvPtrRestore            ;restore screen under pointer

;/*
;**
;**   We're ready to draw the pointer at its new location now
;**
;*/

draw_ready:
        cmp     dl,1                    ;graphics mode?
        ja      short draw_text         ;no
        call    vvPtrDrawGraph          ;yes, call graphics pointer code
        jmp     draw_done

draw_text:
        mov     eax,[yNew]
        mov     ecx,[ebx].nbVRAMRow
        test    [ebx].PtrData.flPtrVideo,PTR_HARDWARE
        jz      short draw_soft
       IFNDEF MONO
        mov     edx,PORT_COLRCRTINDX
       ENDIF
       IFDEF  EGAVGA
        test    [ebx].regMiscOut,MISCOUT_COLRPORTS
        jnz     short draw_hard
       ENDIF
       IFNDEF CGA
        mov     edx,PORT_MONOCRTINDX
       ENDIF

draw_hard:
        shr     ecx,1
        push    edx
        mul     ecx
        pop     edx
        add     eax,[xNew]              ;add column
        push    eax
        mov     al,REG_CRTCURLOCHI
        OUTW    dx,ax
        mov     [ebx].aregCRTData[REG_CRTCURLOCHI],ah
        pop     eax
        mov     ah,al
        mov     al,REG_CRTCURLOCLO
        OUTW    dx,ax
        mov     [ebx].aregCRTData[REG_CRTCURLOCLO],ah
        and     [ebx].PtrData.flPtrVideo,NOT PTR_HWDIRTY
        jmp     short draw_done

draw_soft:
        mul     ecx                     ;multiply row by row size
        add     eax,[xNew]              ;add column
        add     eax,[xNew]              ;add column again (for attributes)
        mov     [ebx].PtrData.offScrBuff,eax    ;save offset of pointer
        mov     esi,eax
       IFDEF CGA
        call    vvPtrGetWord            ;retrieve data under pointer
       ELSE
        mov     ax,[edi+esi]            ;retrieve data under pointer
       ENDIF
        mov     [ebx].PtrData.ulTextScrSave,eax ;and save it
        mov     ecx,[ebx].PtrData.ulTextANDMaskVideo
        mov     edx,[ebx].PtrData.ulTextXORMaskVideo
        and     eax,ecx
        xor     eax,edx
        mov     ch,dl

       IFDEF CGA
        call    vvPtrPutWord            ;update the character position
       ELSE
        cmp     cx,00FFh                ;both char/attr to change?
        je      short draw_attronly     ;no
        mov     [edi+esi],al            ;yes
draw_attronly:
        mov     [edi+esi+1],ah
       ENDIF

draw_done:
        mov     eax,[xNew]
        mov     [ebx].PtrData.xCell,eax
        mov     eax,[yNew]
        mov     [ebx].PtrData.yCell,eax
        or      [ebx].PtrData.flPtrVideo,PTR_DRAWN

draw_exit:
        RestoreReg <edi,esi,ebx>
        ExitProc
EndProc   vvPtrDraw


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrErase()
;*
;* DESCRIPTION   = Erase mouse pointer.
;*
;* INPUT         = hvdm -> VDM
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrErase
        ArgVar  hvdm,HVDM

        EnterProc
        SaveReg <ebx,esi,edi>
        mov     ebx,[hvdm]              ;EBX -> VDM data
        movzx   edx,VDMBase.rb_wVLen[ebx]                               ;        
        movzx   eax,VDMBase.rb_bVPage[ebx] ;use BIOS CRT page           ;          
        add     ebx,FLAToffset VDMData

        mov     edi,[pPhysVRAM]         ;EDI -> VRAM (thru uvirt alias)
        add     edi,[ebx].offPhysVRAMActive

        mul     edx                     ;BIOS defines size of video page;        
        add     edi,eax                                                 ;        

        mov     edx,[ebx].PtrData.vmssVideo.vmss_ulCellWidth
        or      edx,[ebx].PtrData.vmssVideo.vmss_ulCellHeight

        call    vvPtrRestore            ;restore screen under pointer

        RestoreReg <edi,esi,ebx>
        ExitProc
EndProc   vvPtrErase


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrRestore()
;*
;* DESCRIPTION   = Restore screen under mouse pointer.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDX == graphics mode indicator
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrRestore
        test    [ebx].PtrData.flPtrVideo,PTR_DRAWN  ;pointer up?
        jz      short restore_exit      ;no
        test    [ebx].PtrData.flPtrVideo,PTR_HARDWARE
        jnz     short restore_exit      ;hardware cursor cannot be hidden

        cmp     dl,1                    ;graphics mode?
        ja      short restore_text      ;no
        call    vvPtrEraseGraph         ;yes, call graphics pointer code
        jmp     short restore_done

restore_text:
        mov     esi,[ebx].PtrData.offScrBuff
        mov     eax,[ebx].PtrData.ulTextScrSave

       IFDEF CGA
        sub     ecx,ecx                 ;restore whole word (doesn't matter)
        call    vvPtrPutWord            ;update the character position
       ELSE
        mov     [edi+esi],ax            ;update the character position
       ENDIF

restore_done:
        and     [ebx].PtrData.flPtrVideo,NOT PTR_DRAWN

restore_exit:
        ExitProc
EndProc   vvPtrRestore

        EndCode     PRIVATE,SWAP,PASCAL


       IFDEF CGA

        DefCode     EXPORT,GLOBAL,PASCAL

        DefFn       vvPtrGetWord
        DefFn       vvPtrPutByte
        DefFn       vvPtrPutWord
        DefFn       vvPtrWaitRetrace

;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrGetWord()
;*
;* DESCRIPTION   = Read a word of VRAM during retrace.
;*
;*                 Note that retrace considerations are only required on a CGA, and
;*                 only in 80-column text modes.
;*
;* INPUT         = EDI   -> VRAM alias
;*                 ESI   == VRAM offset
;*
;* OUTPUT        = AX   == word at specified offset
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrGetWord
        call    vvPtrWaitRetrace
        mov     ax,[edi+esi]
        sti
        ExitProc
EndProc   vvPtrGetWord


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutByte()
;*
;* DESCRIPTION   = Write a byte of VRAM during retrace
;*
;*                 Note that retrace considerations are only required on a CGA, and
;*                 only in 80-column text modes.
;*
;* INPUT         = AL    == byte
;*                 EDI   -> VRAM alias
;*                 ESI   == VRAM offset
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutByte
        call    vvPtrWaitRetrace
        mov     [edi+esi],al
        sti
        ExitProc
EndProc   vvPtrPutByte


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutWord()
;*
;* DESCRIPTION   = Write a word of VRAM during retrace.
;*
;*                 Note that retrace considerations are only required on a CGA, and
;*                 only in 80-column text modes.
;*
;* INPUT         = AX    == word
;*                 CX    == 00FFh to write high byte only, any other value to write word
;*                 EDI   -> VRAM alias
;*                 ESI   == VRAM offset
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutWord
        call    vvPtrWaitRetrace
        cmp     cx,00FFh                ;both char/attr to change?
        je      short putw_attronly     ;no
        mov     [edi+esi],al            ;yes
putw_attronly:
        mov     [edi+esi+1],ah
        sti
        ExitProc
EndProc   vvPtrPutWord


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrWaitRetrace()
;*
;* DESCRIPTION   = Wait for retrace interval
;*
;*                 Note that retrace considerations are only required on a CGA, and
;*                 only in 80-column text modes.
;*
;* INPUT         = None
;*
;* OUTPUT        = Interrupts disabled (cannot be called from any swappable code)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrWaitRetrace
        push    eax
        push    edx
        mov     dx,PORT_COLRSTATUS1
wait1:
        in      al,dx
        test    al,STATUS1_HORZRTRC
        jnz     short wait1
        cli
wait2:
        in      al,dx
        test    al,STATUS1_HORZRTRC
        jz      short wait2
        pop     edx
        pop     eax
        ExitProc
EndProc   vvPtrWaitRetrace

        EndCode     EXPORT,GLOBAL,PASCAL

       ENDIF


        DefCode     PRIVATE,SWAP,PASCAL


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrDrawGraph
;*
;* DESCRIPTION   = Draws pointers for graphics modes.
;*
;*                 Called by vvPtrDraw to draw a pointer in graphics mode.
;*                 We have already verified that the video mode is supported.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*                 EBP -> stack frame from vvPtrDraw
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrDrawGraph
        mov     eax,[xNew]
        sub     eax,[ebx].PtrData.xHotVideo
        cmp     [ebx].nBitsPixel,2          ;CGA mode?
        jne     short drawg_notcga          ;no
        mov     eax,[xNew]
        mov     ecx,[ebx].PtrData.xHotVideo
        shr     ecx,1                       ;subtract only 1/2 hot value
        sub     eax,ecx
        cmp     [ebx].PtrData.vmssVideo.vmss_ulWidth,640        ;          
        je      short drawg_notcga                              ;          
        add     eax,eax                     ;then convert to 640-point grid
drawg_notcga:
        mov     [ebx].PtrData.xCorner,eax

        mov     eax,[yNew]
        sub     eax,[ebx].PtrData.yHotVideo
        mov     [ebx].PtrData.yCorner,eax

       IFDEF EGAVGA
        call    vvPtrSetState
       ENDIF
        call    vvPtrEraseOldPointer        ;erase old ptr from screen/buffer
        call    vvPtrCopyScrSaveToPtrSave   ;save area under new pointer

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jnz     short drawg_norotate        ;no need to rotate masks
        call    vvPtrRotateMasks            ;rotate pointer masks into place

drawg_norotate:
        call    vvPtrPutPointerInScrSave    ;generate new pointer in buffer
        call    vvPtrCopyScrSaveToScreen    ;copy buffer with pointer onto screen
       IFDEF EGAVGA
        call    vvPtrRestoreState
       ENDIF

        ExitProc
EndProc   vvPtrDrawGraph


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrEraseGraph
;*
;* DESCRIPTION   = Erases pointers for graphics modes
;*
;*                 Called by vvPtrErase to erase a pointer in graphics mode.
;*                 We have already verified that the video mode is supported.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*                 EBP -> stack frame from vvPtrDraw
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrEraseGraph
        test    [ebx].PtrData.flPtrVideo,PTR_PTRSAVE;valid image in ptrsave buffer?
        jz      short eraseg_done           ;no, just return

       IFDEF EGAVGA
        call    vvPtrSetState
       ENDIF
        call    vvPtrCopyPtrSaveToScreen
       IFDEF EGAVGA
        call    vvPtrRestoreState
       ENDIF
        and     [ebx].PtrData.flPtrVideo,NOT PTR_PTRSAVE

eraseg_done:
        ExitProc
EndProc   vvPtrEraseGraph


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrEraseOldPointer
;*
;* DESCRIPTION   = Erase Old Pointer
;*
;*                 The old pointer is erased from the screen, and the screen buffer
;*                 is updated as needed.  This may be performed in a couple of different
;*                 ways:
;*
;*                 If a pointer isn't drawn on the screen, the screen buffer is filled
;*                 from the screen in preparation for the forthcoming draw.
;*
;*                 If a pointer is drawn on the screen and the new and old pointers will
;*                 fit within the same screen buffer, the screen buffer is filled from
;*                 the screen and the old pointer is removed from the screen buffer by
;*                 writing over it with the save buffer.
;*
;*                 If a pointer is drawn on the screen and the new and old pointers will
;*                 NOT fit within the same screen buffer, the old pointer is removed
;*                 from the screen by writing over it with the save buffer, and then the
;*                 screen buffer is filled from the screen in preparation for the
;*                 forthcoming draw.
;*
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrEraseOldPointer
        call    vvPtrGetScrBuffOffset   ;see if old and new pointers will fit
                                        ;in the buffer
        test    [ebx].PtrData.flPtrVideo,PTR_PTRSAVE;is old image invalid?
        jz      short eoc1              ; yes, just read new area into scrsave

        and     [ebx].PtrData.flPtrVideo,NOT PTR_PTRSAVE
        or      edx,edx                 ;if EDX == 0, both pointers will fit
        jz      short eoc2              ; in the same buffer

;/*
;**
;**   Both pointers will NOT fit in the screen buffer.  Restore area under
;**   the pointer, and refill the screen buffer based on the new pointer (x,y)
;**
;*/

        call    vvPtrCopyPtrSaveToScreen    ;restore what was under the pointer
eoc1:   call    vvPtrCopyScreenToScrSave    ;copy from screen to screen buffer
        ret

;/*
;**
;**   Both pointers can be contained within the screen buffer.  Reread the
;**   information from the screen based on the new pointer (x,y) into the screen
;**   buffer and remove the old pointer from the screen buffer.
;**
;*/

eoc2:   call    vvPtrCopyScreenToScrSave    ;read screen into screen buffer
        call    vvPtrCopyPtrSaveToScrSave   ;remove pointer from screen buffer
        ret
EndProc   vvPtrEraseOldPointer


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrRotateMasks
;*
;* DESCRIPTION   = Rotate masks.
;*
;*                 The pointer masks are rotated to be aligned for the new (x,y).
;*                 The rotate is performed as a single-bit shift of the entire mask.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        =
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrRotateMasks
        mov     dl,07h                  ;DL == mask for 3 LSBs
        mov     al,byte ptr [ebx].PtrData.xCorner
        and     al,dl                   ;AL == 3 lsb's of X coordinate
                                        ;DL == previous pointer rotate count
        and     dl,byte ptr [ebx].PtrData.nBitsRotate
                                        ;save new pointer rotate count
        mov     byte ptr [ebx].PtrData.nBitsRotate,al

        sub     al,dl                   ;AL == rotate delta
        jz      short rmexit            ;branch if already aligned

        push    edi
        jl      short rmleft            ;branch if new < old (i.e rotate left)

        push    eax                     ;save rotate count
        lea     esi,[ebx].PtrData.abANDMask
        call    vvPtrRotateRight        ;rotate the AND mask right

        pop     eax                     ;restore rotate count
        lea     esi,[ebx].PtrData.abXORMask
        call    vvPtrRotateRight        ;rotate the XOR mask right
        jmp     short rmdone
rmleft:
        neg     al                      ;make rotate count positive
        push    eax                     ;save rotate count
        lea     esi,[ebx].PtrData.abANDMask
        call    vvPtrRotateLeft         ;rotate the AND mask left

        pop     eax                     ;restore rotate count
        lea     esi,[ebx].PtrData.abXORMask
        call    vvPtrRotateLeft         ;rotate the XOR mask left
rmdone:
        pop     edi
rmexit:
        ExitProc
EndProc   vvPtrRotateMasks


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrRotateRight
;*
;* DESCRIPTION   = Rotate right
;*
;* INPUT         =
;* OUTPUT        =
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrRotateRight

;/*
;** Rotate the given mask right (al) bits.
;** If the rotate is more than two bits, use the fast code.
;*/

        cmp     al,3                    ;Use the big rotate code?
        jae     short rr3               ;branch to use big rotate
        mov     dl,al                   ;save rotate count in DL

rr1:    mov     edi,esi                 ;EDI points to mask
        mov     ecx,PTRSAVSIZE          ;ECX == # of bytes in a pointer mask
        align   4                                                       ;          
rr2:    rcr     byte ptr [edi],1        ;shift a byte / bit 0 goes to carry
                                        ;carry goes to bit 7
        inc     edi                     ;point to next byte
        hploop  rr2                     ;do it again

;/*
;**
;**   Now finish the rotate by setting the first bit in the first byte to the
;**   bit shifted out of the last byte.  Use Waltm's magic to do this with a
;**   couple of nifty rotates.
;**
;*/

        mov     al,[esi]                ;AL == first byte (already shifted)
        rcl     al,1                    ;dump unwanted bit from bit 7
        ror     al,1                    ;move wanted bit into bit 7
        mov     [esi],al                ;save new byte

        dec     dl                      ;decrement rotate count
        jnz     short rr1               ;branch if it's got to be done again
        ret

;/*
;**
;**   The big rotate rotates 'n' bits at a time.  Time will be saved using this
;**   code for rotates of over two bits.
;**
;*/

rr3:    push    esi                     ;save first byte pointer for later
        mov     edi,esi                 ;set pointers
        mov     cl,al                   ;set rotate count
        xor     dl,dl                   ;zero initial previous bits
        mov     dh,PTRSAVSIZE           ;DH == # of bytes in each pointer mask
        align   4                                                       ;          
rr4:    xor     ah,ah                   ;zero LSB for the shift
        lodsb                           ;get a mask byte & update in pointer
        ror     ax,cl                   ;rotate by shift count
        or      al,dl                   ;or bytes from previous shift
        mov     dl,ah                   ;save bits for next shift
        stosb                           ; store new byte & update out pointer
        dec     dh                      ;decrement remaining mask byte count
        jnz     short rr4               ;loop until entire mask rotated

;/*
;**
;**   Now finish the rotate by setting the first byte's high order bits to the
;**   bits shifted out of the last byte.
;**
;*/

        pop     esi                     ;ESI -> first byte
        or      byte ptr [esi],dl       ;OR byte from last shift
        ExitProc
EndProc   vvPtrRotateRight


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrRotateLeft
;*
;* DESCRIPTION   = Rotate left
;*
;* INPUT         =
;* OUTPUT        =
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrRotateLeft
        mov     dl,al                   ;save rotate count in DL
        add     esi,PTRSAVSIZE          ;ESI -> last byte (almost!)
        dec     esi                     ;adjust for overshoot
        cmp     al,3                    ;use the big rotate code?
        jae     short rl3               ;  yes, it will be faster

rl1:    mov     edi,esi                 ;EDI -> last byte
        mov     ecx,PTRSAVSIZE          ;ECX == # of bytes in pointer mask
        align   4                                                       ;          
rl2:    rcl     byte ptr [edi],1        ;shift a byte / bit 0 goes to carry
                                        ;carry goes to bit 7
        dec     edi                     ;go to previous byte
        hploop  rl2

;/*
;**
;**   Now finish the rotate by setting the last bit in the last byte to the
;**   bit shifted out of the first byte.  Use Waltm's magic to do this with a
;**   couple of nifty rotates.
;**
;*/

        mov     al,[esi]                ;AL == last byte
        rcr     al,1                    ;remove unwanted bit, add in good bit
        rol     al,1                    ;get good bit into bit 0
        mov     [esi],al                ;save the good byte with the good bit
        dec     dl                      ;decrement rotate count
        jnz     short rl1               ;branch if it's got to be done again
        ret

;/*
;**
;**   The big rotate rotates 'n' bits at a time.  Time will be saved using this
;**   code for rotates of over two bits.
;**
;*/

rl3:    std                             ;copy backwards
        push    esi                     ;save last point pointer for later
        mov     edi,esi                 ;EDI -> last byte
        mov     cl,al                   ;set rotate count
        mov     dh,PTRSAVSIZE           ;DH  == # of bytes in a pointer mask
        xor     dl,dl                   ;zero initial previous bits
        align   4                                                       ;          
rl4:    xor     ah,ah                   ;zero LSB for the shift
        lodsb                           ;get a mask byte & update in pointer
        shl     ax,cl                   ;shift as needed
        or      al,dl                   ;get previous unused bits
        mov     dl,ah                   ;save new unused bits
        stosb                           ;save byte and update pointer
        dec     dh                      ;decrement remaining mask byte count
        jnz     short rl4               ;loop until entire mask rotated

;/*
;**
;**    Now finish the rotate by setting the last byte's low order bits to the
;**    bits shifted out of the first byte.
;**
;*/

        pop     esi                     ;SI <- last byte pointer
        cld                             ;direction back to normal
        or      byte ptr [esi],dl       ;OR in byte from last shift
        ExitProc
EndProc   vvPtrRotateLeft


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutPointerInScrSave
;*
;* DESCRIPTION   = Current pointer is ANDed and XORed into the current local buffer.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutPointerInScrSave
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      short vvPtrPutPointerInScrSave1

        lea     eax,[ebx].PtrData.abPtrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrPutPointerInScrSave1

        lea     eax,[ebx].PtrData.abPtrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrPutPointerInScrSave1
        ExitProc
EndProc   vvPtrPutPointerInScrSave


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutPointerInScrSave1
;*
;* DESCRIPTION   = Worker for vvPtrPutPointerInScrSave
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutPointerInScrSave1
        mov     eax,[ebx].PtrData.xCorner       ;get screen coordinates upper left bit
        mov     esi,[ebx].PtrData.yCorner       ;compute addr of screen buffer
        call    vvPtrGetOffsetIntoScrSave

        push    ebx                     ;ECX == ptrht
        push    edi                     ;ESI == offset of upper left bit
        add     edi,esi                 ;EDI -> screen buffer

      IFDEF EGAVGA
       IFDEF VDDSTRICT
        mov     eax,[rlePtrGDCMode.rle_pple] ;                  /*          */
;       mov     eax,[esi].rle_port      ;                       /*          */
;       and     eax,PORT_MASK           ;                       /*          */
        mov     eax,[eax].rle_pple      ;                       /*          */
        mov     esi,[eax].ppleBRegIndx  ;                       /*          */
        and     esi,esi                 ;                       /*          */
        jz      @f                      ;                       /*          */
        mov     eax,esi                 ;                       /*          */
@@:                                     ;                       /*          */
        movzx   eax,[eax].sTrapBRegInfo.portTrap.off ;          /*          */
;                                       ;                       /*          */
        cmp     eax,PORT_GDCINDX
        jne     short ppss_error
        cmp     [rlePtrGDCMode.rle_indx],REG_GDCMODE ;          /*          */
        je      short ppss_ok
       ppss_error:
        ASSERT  <"Invalid PtrState table",10>,<00>
       ppss_ok:
       ENDIF
        mov     esi,[ebx].PtrData.iPtrMode
        mov     ah,[rlePtrGDCMode.rle_bMode][esi] ;             /*          */
      ENDIF ;EGAVGA
        mov     edx,[ebx].PtrData.flPtrVideo    ;EDX == pointer flags
        push    FLAToffset ppss_return
        push    [ebx].PtrData.pfnPutImage       ;push address of where we need to go
        lea     esi,[ebx].PtrData.abANDMask     ;ESI -> AND mask
        lea     ebx,[ebx].PtrData.abXORMask     ;EBX -> XOR mask
        ret                             ;dispatch to appropriate "put" routine

ppss_return:
        pop     edi
        pop     ebx
        ExitProc
EndProc   vvPtrPutPointerInScrSave1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutNormal
;*
;* DESCRIPTION   = Put the pointer into the screen buffer (used for all display modes
;*                 except 4-plane EGA modes).
;*
;* INPUT         = AH  == default GDC mode setting
;*                 ECX == pointer height
;*                 EDX == pointer flags
;*                 EDI == offset of the upper left bit in screen buffer
;*                 ESI == offset of 1st byte of the AND mask
;*                 EBX == offset of 1st byte of the XOR mask
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutNormal
        REPT    PTRSAVWD/2
        lodsw                           ;get AND mask
        and     eax,[edi]               ;AND it with the screen buffer
        xor     eax,[ebx]               ;XOR it with XOR mask
        stosw                           ;put the result into the screen buffer
        add     ebx,2                   ;point to next XOR mask word
        ENDM
                                        ;do one more byte to handle masks shift
        IF      PTRSAVWD AND 1
        lodsb                           ;get AND mask
        and     al,[edi]                ;AND it with the screen buffer
        xor     al,[ebx]                ;XOR it with XOR mask
        stosb                           ;put the result into the screen buffer
        inc     ebx                     ;point to next XOR mask byte
        ENDIF

        add     edi,2                   ;point to next screen buffer location
        hploop  vvPtrPutNormal          ;branch if more scan lines to do

        ExitProc
EndProc   vvPtrPutNormal


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutEGA
;*
;* DESCRIPTION   = Just like vvPtrPutNormal, but used for EGA operation when using
;*                 the 4-64k plane modes (as opposed to the 4-16k chained-plane modes).
;*
;* INPUT         = AH  == default GDC mode setting
;*                 ECX == pointer height
;*                 EDX == pointer flags
;*                 EDI == offset of the upper left bit in screen buffer
;*                 ESI == offset of 1st byte of the AND mask
;*                 EBX == offset of 1st byte of the XOR mask
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutEGA
        push    eax                     ;save GDC setting
        and     ah,NOT GDCMODE_WRITE1
        mov     al,REG_GDCMODE
        test    edx,PTR_INVERSE         ;test for inverse before we trash EDX
        mov     edx,PORT_GDCINDX
        OUTW    dx,ax                   ;force write mode 1
        jnz     short ep4               ;now jump if inverse pointer

        mov     al,REG_GDCBITMASK       ;select GDC bitmask reg
        out     dx,al                   ;for subsequent output
        inc     edx
        align   4                                                       ;          
ep1:
        REPT    PTRSAVWD
        lodsb
        out     dx,al
        mov     ah,[edi]                ;latch all planes first
        mov     al,[ebx]
        stosb
        inc     ebx
        ENDM
        add     edi,SCRSAVWD-PTRSAVWD
        hploop  ep1

        mov     al,GDCBITMASK_ALL       ;normal bit mask again
        out     dx,al
        jmp     short ep9

;/*
;**
;**   Inverse pointer code starts here
;**
;*/

ep4:    mov     al,REG_GDCROTATE
        out     dx,al
        inc     edx
        align   4                                                       ;          
ep5:
        REPT    PTRSAVWD
        mov     al,08h
        out     dx,al
        mov     al,[edi]                ;latch all planes first
        lodsb
        mov     [edi],al
        mov     al,GDCROTATE_XOR
        out     dx,al
        mov     al,[edi]                ;latch all planes first
        mov     al,[ebx]
        stosb
        inc     ebx
        ENDM
        add     edi,SCRSAVWD-PTRSAVWD
        hploop  ep5
ep9:
        dec     edx
        pop     eax                     ;restore GDC setting
        mov     al,REG_GDCMODE
        OUTW    dx,ax                   ;restore the GDC mode register
        ExitProc
EndProc   vvPtrPutEGA


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrPutVGA
;*
;* DESCRIPTION   = Just like vvPtrPutNormal, but is used for VGA operation when using
;*                 the mode 13h.  ANDs and XORs the pointer shape into the screen buffer.
;*
;* INPUT         = AH  == default GDC mode setting
;*                 ECX == pointer height
;*                 EDX == pointer flags
;*                 EDI == offset of the upper left bit in screen buffer
;*                 ESI == offset of 1st byte of the AND mask
;*                 EBX == offset of 1st byte of the XOR mask
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrPutVGA
vp1:    mov     edx,BYTEPTRSAVWD/8      ;EDX == width in 8-pixel chunks

vp2:    push    ebx                     ;save XOR mask pointer
        mov     bh,[ebx]                ;BH == XOR mask pointer
        mov     bl,[esi]                ;BL == AND mask pointer
        mov     ah,8                    ;doing 8 bytes at a time

vp3:    mov     al,[edi]                ;AL == buffer byte
        shl     bl,1                    ;carry <- AND mask bit
        jnc     short vp4               ;branch if AND mask bit is 0
        xor     al,al                   ;AND with 0's is 0
vp4:    shl     bh,1                    ;carry <- XOR mask bit
        jnc     short vp5               ; 
        xor     al,0Fh                  ;XOR with 0Fh for good color
vp5:    stosb                           ;buffer byte <- al & increment pointer
        dec     ah                      ;all 8 bits/bytes done ?
        jne     short vp3               ;branch until all bits/bytes done

        pop     ebx                     ;restore XOR mask pointer
        inc     ebx                     ;point to next XOR mask word
        inc     esi                     ;point to next AND mask word
        dec     edx                     ;decrement remaining width counter
        jnz     short vp2               ;branch if more in current scan line
                                        ;skip to next buffer location
        add     edi,BYTESCRSAVWD-BYTEPTRSAVWD
        hploop  vp1                     ;loop until all scan lines done
        ExitProc
EndProc   vvPtrPutVGA


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyPtrSaveToScreen
;*
;* DESCRIPTION   = Restore the contents of the old screen to
;*                 PtrData.xCornerPrev,PtrData.yCornerPrev
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyPtrSaveToScreen
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      vvPtrCopyPtrSaveToScreen1   ;                   /*          */

        mov     al,SEQMAP_PLANE0+SEQMAP_PLANE1
        call    vvPtrEnableWriteMaps    ;enable planes 0 and 1

        lea     eax,[ebx].PtrData.abPtrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        call    vvPtrCopyPtrSaveToScreen1

        mov     al,SEQMAP_PLANE0+SEQMAP_PLANE1
        call    vvPtrEnableWriteMaps    ;enable planes 2 and 3

        lea     eax,[ebx].PtrData.abPtrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        call    vvPtrCopyPtrSaveToScreen1

      IFDEF EGAVGA

       IFDEF VDDSTRICT
;       mov     eax,[rlePtrSEQMapMask.rle_port] ;               /*          */
;       and     eax,PORT_MASK
        mov     eax,[rlePtrSEQMapMask.rle_pple] ;               /*          */
        mov     esi,[eax].ppleBRegIndx  ;                       /*          */
        and     esi,esi                 ;                       /*          */
        jz      @f                      ;                       /*          */
        mov     eax,esi                 ;                       /*          */
@@:                                     ;                       /*          */
        movzx   eax,[eax].sTrapBRegInfo.portTrap.off ;          /*          */
;                                       ;                       /*          */
        cmp     eax,PORT_SEQINDX
        jne     short cpss_error
        cmp     [rlePtrSEQMapMask.rle_indx],REG_SEQMAPMASK ;    /*          */
        je      short cpss_ok
       cpss_error:
        ASSERT  <"Invalid PtrState table",10>,<00>
       cpss_ok:
       ENDIF

        mov     esi,[ebx].PtrData.iPtrMode
        mov     al,[rlePtrSEQMapMask.rle_bMode][esi] ;          /*          */
        call    vvPtrEnableWriteMaps    ;restore default write mask for mode
      ENDIF ;EGAVGA

        ExitProc
EndProc   vvPtrCopyPtrSaveToScreen


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyPtrSaveToScreen1
;*
;* DESCRIPTION   = Worker for vvPtrCopyPtrSaveToScreen
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyPtrSaveToScreen1
        push    edi
        mov     eax,[ebx].PtrData.xCornerPrev
        mov     esi,[ebx].PtrData.yCornerPrev

        push    eax                     ;1st two stack parms for
        push    esi                     ; vvPtrCopyBufferToScreen

        call    vvPtrGetScreenOffset    ;ESI == corresponding offset

        add     esi,edi                 ;ESI -> restore area
        mov     edx,edi
        add     edx,[ebx].nbPhysVRAMVisible
        add     edi,[ebx].PtrData.offPtrSave    ;EDI -> ptrsave area
        xchg    esi,edi                 ;reverse source and destination
        sub     ecx,ecx

        push    PTRSAVHT                ;3rd stack parm for vvPtrCopyBuffer...
        call    vvPtrCopyBufferToScreen
        pop     edi
        ExitProc
EndProc   vvPtrCopyPtrSaveToScreen1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyPtrSaveToScrSave
;*
;* DESCRIPTION   = Restore the contents of the old screen to
;*                 PtrData.xCornerPrev,PtrData.yCornerPrev
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyPtrSaveToScrSave
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      short vvPtrCopyPtrSaveToScrSave1

        lea     eax,[ebx].PtrData.abPtrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyPtrSaveToScrSave1

        lea     eax,[ebx].PtrData.abPtrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyPtrSaveToScrSave1
        ExitProc
EndProc   vvPtrCopyPtrSaveToScrSave


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyPtrSaveToScrSave1
;*
;* DESCRIPTION   = Worker for vvPtrCopyPtrSaveToScrSave
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyPtrSaveToScrSave1
        mov     eax,[ebx].PtrData.xCornerPrev
        mov     esi,[ebx].PtrData.yCornerPrev
        call    vvPtrGetOffsetIntoScrSave

        push    edi
        add     esi,edi                 ;adjust scrsave offset
        add     edi,[ebx].PtrData.offPtrSave
        xchg    esi,edi                 ;reverse source and destination

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short cctb2             ;jump if not byte-pixel mode
        align   4                                                       ;          
cctb1:  push    ecx                     ;byte-pixel mode
        mov     ecx,BYTEPTRSAVWD
        rep     movsb
        pop     ecx
        add     edi,eax
        hploop  cctb1
        jmp     short cctb9
        align   4                                                       ;          
cctb2:  REPT    PTRSAVWD                ;bit-pixel mode
        movsb
        ENDM
        add     edi,eax
        hploop  cctb2

cctb9:  pop     edi
        ExitProc
EndProc   vvPtrCopyPtrSaveToScrSave1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScrSaveToScreen
;*
;* DESCRIPTION   = The contents of the screen buffer is copied to the screen.
;*                 The contents are clipped to the screen as needed.
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScrSaveToScreen
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      vvPtrCopyScrSaveToScreen1   ;                   /*          */

        mov     al,SEQMAP_PLANE0+SEQMAP_PLANE1
        call    vvPtrEnableWriteMaps    ;enable planes 0 and 1

        lea     eax,[ebx].PtrData.abScrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScrSaveToScreen1

        mov     al,SEQMAP_PLANE2+SEQMAP_PLANE3
        call    vvPtrEnableWriteMaps    ;enable planes 2 and 3

        lea     eax,[ebx].PtrData.abScrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScrSaveToScreen1

      IFDEF EGAVGA
       IFDEF VDDSTRICT
;       mov     eax,[rlePtrSEQMapMask.rle_port] ;               /*          */
;       and     eax,PORT_MASK           ;                       /*          */
        mov     eax,[rlePtrSEQMapMask.rle_pple] ;               /*          */
        mov     esi,[eax].ppleBRegIndx  ;                       /*          */
        and     esi,esi                 ;                       /*          */
        jz      @f                      ;                       /*          */
        mov     eax,esi                 ;                       /*          */
@@:                                     ;                       /*          */
        movzx   eax,[eax].sTrapBRegInfo.portTrap.off ;          /*          */
;                                       ;                       /*          */
        cmp     eax,PORT_SEQINDX
        jne     short csss_error
        cmp     [rlePtrSEQMapMask.rle_indx],REG_SEQMAPMASK ;    /*          */
        je      short csss_ok
       csss_error:
        ASSERT  <"Invalid PtrState table",10>,<00>
       csss_ok:
       ENDIF

        mov     esi,[ebx].PtrData.iPtrMode
        mov     al,[rlePtrSEQMapMask.rle_bMode][esi] ;          /*          */
        call    vvPtrEnableWriteMaps    ;restore default write mask for mode
      ENDIF ;EGAVGA
        ExitProc
EndProc    vvPtrCopyScrSaveToScreen


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScrSaveToScreen1
;*
;* DESCRIPTION   = Worker for vvPtrCopyScrSaveToScreen
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScrSaveToScreen1
        push    edi

        mov     esi,edi
        add     esi,[ebx].PtrData.offScrSave    ;ESI == pointer buffer offset
        mov     edx,edi
        add     edx,[ebx].nbPhysVRAMVisible
        add     edi,[ebx].PtrData.offScrBuff    ;EDI == display offset

        mov     ecx,SCRSAVWD-PTRSAVWD   ;ECX == (scrsave width - ptrsave width)

        push    [ebx].PtrData.xScrBuff  ;push screen co-ordinates
        push    [ebx].PtrData.yScrBuff
        push    [ebx].PtrData.htScrBuff
        call    vvPtrCopyBufferToScreen

        pop     edi
        ExitProc
EndProc   vvPtrCopyScrSaveToScreen1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScrSaveToPtrSave
;*
;* DESCRIPTION   = Worker for vvPtrCopyScrSaveToScreen
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScrSaveToPtrSave
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      short vvPtrCopyScrSaveToPtrSave1

        lea     eax,[ebx].PtrData.abPtrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScrSaveToPtrSave1

        lea     eax,[ebx].PtrData.abPtrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offPtrSave,eax
        lea     eax,[ebx].PtrData.abScrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScrSaveToPtrSave1
        ExitProc
EndProc   vvPtrCopyScrSaveToPtrSave


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScrSaveToPtrSave1
;*
;* DESCRIPTION   = Worker for vvPtrCopyScrSaveToPtrSave
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScrSaveToPtrSave1
        mov     eax,[ebx].PtrData.xCorner
        mov     esi,[ebx].PtrData.yCorner
        mov     [ebx].PtrData.xCornerPrev,eax
        mov     [ebx].PtrData.yCornerPrev,esi
        call    vvPtrGetOffsetIntoScrSave

        push    edi
        add     esi,edi                 ;adjust scrsave offset
        add     edi,[ebx].PtrData.offPtrSave

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short cbtc2             ;jump if not byte-pixel mode
        align   4                                                       ;          
cbtc1:  push    ecx                     ;byte-pixel mode
        mov     ecx,BYTEPTRSAVWD
        rep     movsb
        pop     ecx
        add     esi,eax
        hploop  cbtc1
        jmp     short cbtc9
        align   4                                                       ;          
cbtc2:  REPT    PTRSAVWD                ;bit-pixel mode
        movsb
        ENDM
        add     esi,eax
        hploop  cbtc2

cbtc9:  pop     edi
        or      [ebx].PtrData.flPtrVideo,PTR_PTRSAVE

        ExitProc
EndProc   vvPtrCopyScrSaveToPtrSave1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScreenToScrSave
;*
;* DESCRIPTION   = The contents of the given region of the screen where the
;*                 pointer is/will go, are copied into the screen buffer.
;*
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScreenToScrSave
        test    [ebx].PtrData.flPtrVideo,PTR_ODDEVEN
        jz      short vvPtrCopyScreenToScrSave1

        mov     al,PLANE0
        call    vvPtrEnableReadMap

        lea     eax,[ebx].PtrData.abScrSave1
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScreenToScrSave1

        mov     al,PLANE2
        call    vvPtrEnableReadMap

        lea     eax,[ebx].PtrData.abScrSave2
        sub     eax,edi
        mov     [ebx].PtrData.offScrSave,eax
        call    vvPtrCopyScreenToScrSave1
        ExitProc
EndProc   vvPtrCopyScreenToScrSave


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyScreenToScrSave1
;*
;* DESCRIPTION   = Worker for vvPtrCopyScreenToScrSave.  Had to throw more
;*                 code into this than the DOS function had, because they're
;*                 sloppy about what theycopy and do not clip the source
;*                 address;  for the Video VDD, we must catch source addresses
;*                 outside the normal VRAM range, because we're using an alias
;*                 that may not be bordered by valid memory, and we musn't take
;*                 a page fault (03-Aug-89 ).
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyScreenToScrSave1
        push    edi
        push    ebp

        mov     esi,edi
        add     esi,[ebx].PtrData.offScrBuff    ;ESI -> screen area to copy
        mov     edx,edi
        add     edx,[ebx].nbPhysVRAMVisible
        add     edi,[ebx].PtrData.offScrSave    ;EDI -> scrsave buffer
        mov     ecx,[ebx].PtrData.htScrBuff     ;ECX == # scan lines in screen buffer

        mov     eax,SCRSAVWD            ;EAX = screen buffer width in bytes
        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short cstb0             ;not byte-pixel
        mov     eax,BYTESCRSAVWD
cstb0:  neg     eax
        add     eax,[ebx].nbVRAMRow     ;EAX == byte difference between lines
        mov     ebp,[ebx].offOddBank

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short cstb2
        align   4                                                       ;          
cstb1:  push    ecx
        sub     ecx,ecx
        cmp     esi,[pPhysVRAM]         ;sanity-check source address
        jae     short cstb1a
        mov     ecx,[pPhysVRAM]
        sub     ecx,esi                 ;ECX == # bytes out-of-bounds
        add     esi,ecx                 ;just skip that many
        add     edi,ecx
        neg     ecx
cstb1a: add     ecx,BYTESCRSAVWD
        jg      short cstb1b
        add     esi,ecx                 ;skipped more than we wanted to copy
        add     edi,ecx                 ;so adjust ESI/EDI and skip copy
        sub     ecx,ecx
cstb1b: rep     movsb
        pop     ecx
        add     esi,eax                 ;add offset to 1st byte in next line
        hploop  cstb1
        jmp     short cstb3

cstb2:  cmp     esi,[pPhysVRAM]         ;sanity-check source address
        jae     short cstb2b
        push    ecx
        mov     ecx,[pPhysVRAM]
        sub     ecx,esi                 ;ECX == # bytes out-of-bounds
        add     esi,ecx                 ;just skip that many
        add     edi,ecx
        neg     ecx
        add     ecx,SCRSAVWD
        jg      short cstb2a
        add     esi,ecx                 ;skipped more than we wanted to copy
        add     edi,ecx                 ;so adjust ESI/EDI and skip copy
        sub     ecx,ecx
cstb2a: rep     movsb
        pop     ecx
        jmp     short cstb2c
        align   4                                                       ;          
cstb2b: REPT    SCRSAVWD
        movsb
        ENDM
cstb2c: add     esi,eax                 ;add offset to 1st byte in next scan
        or      ebp,ebp
        jz      short cstb2d
        sub     esi,[ebx].nbVRAMRow     ;handle CGA-style bank offset here
        add     esi,ebp
        cmp     esi,edx
        jb      short cstb2d
        sub     esi,ebp
        sub     esi,ebp
        add     esi,[ebx].nbVRAMRow
cstb2d: hploop  cstb2                   ;do another scan line

cstb3:  pop     ebp
        pop     edi
        ExitProc
EndProc   vvPtrCopyScreenToScrSave1


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrCopyBufferToScreen
;*
;* DESCRIPTION   = Contents of the pointer-save buffer or screen-save buffer
;*                 are copied to the screen.  The contents are clipped to the
;*                 screen as needed.
;*
;* INPUT         = EBX -> VDM instance data
;*                 ECX == extra width to copy:
;*                         (0) for ptrsave update
;*                         (scrsave width - ptrsave width) for scrsave update
;*                 EDX == first address beyond end of screen memory
;*                 ESI == screen buffer or pointer buffer
;*                 EDI == offset of screen memory destination
;*
;*                 xBuff == x-coordinate of buffer (upper-left corner)
;*                 yBuff == y-coordinate of buffer (upper-left corner)
;*                 height == height to copy (PtrData.htScrBuff or MAX_PTRHEIGHT)
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrCopyBufferToScreen
        ArgVar  xBuff,ULONG
        ArgVar  yBuff,ULONG
        ArgVar  height,ULONG

        LocalVar pInvalid,PVOID         ;initialized with EDX
        LocalVar nbLine,ULONG
        LocalVar nbxClip,ULONG

        EnterProc <edx>
        add     ecx,PTRSAVWD            ;ECX == ptrwd or bufwd

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short cbts1             ;not byte-pixel mode?

        shl     ecx,3                   ;convert pointer width to byte/pixel
        .ERRNZ  BYTEPTRSAVWD NE PTRSAVWD*8

cbts1:  mov     eax,[ebx].nbVRAMRow     ;EAX == display offset between lines
        sub     eax,ecx

        mov     edx,ecx                 ;EDX == buffer/pointer width
        mov     [nbLine],ecx            ;save bytes/line

;/*
;**
;**   Check the left hand side of the image to see if clipping must be done.
;**   If it must be done, then clip by adjusting the starting addresses, the
;**   buffer/pointer width, and scan line offset.
;**
;*/

        mov     [nbxClip],0             ;zero clipped horizontal bytes
        mov     ecx,[xBuff]             ;get x-coordinate
        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jnz     short cbts1b            ;byte-pixel mode
        sar     ecx,3                   ;convert bit number to byte offset

cbts1b: or      ecx,ecx                 ;if ECX < 0, left side is off-screen
        jge     short cbts2             ;left side is on-screen

;/*
;**
;**   X value clips left side of screen by -ECX number of bytes.  Clip on the left
;**   by biasing the start of the screen position & screen/pointer buffer to the
;**   right by adding -ECX bytes to each of them.  It's also necessary to adjust the
;**   scan line offset and buffer/pointer width (this is done at cbts3).
;**
;*/

        neg     ecx                     ;ECX == # bytes to clip from left side
        add     edi,ecx                 ;increase offset pos on screen
        add     esi,ecx                 ;increase offset pos in buffer/pointer
        jmp     short cbts3             ;adjust buffer width and scan line offset

;/*
;**
;**   The left side of the buffer/pointer will fit onto the screen without being
;**   clipped. Now see if the right hand side will fit without being clipped.
;**   If it won't then clip by adjusting the buffer/pointer width and scan line
;**   offset.
;**
;*/

cbts2:  add     ecx,[nbLine]            ;add width of buffer/pointer to x value
        sub     ecx,[ebx].nbVRAMRow     ;subtract width of one line
        jle     short cbts4             ;if negative or zero, no clipping

;/*
;**
;**   Adjust the scan line offset and buffer/pointer width by the number of bytes
;**   that need to be clipped (ECX).
;**
;*/

cbts3:  mov     [nbxClip],ecx           ;number of bytes to clip
        add     eax,ecx                 ;increase next scan line increment
        sub     edx,ecx                 ;decrease the width of the buffer
        jle     short cbts14            ;nothing to display - quick exit

;/*
;**
;**   Check the top of the image to see if clipping must be done.
;**   If it must be done, then clip by adjusting the starting address and the
;**   scan line count.  Since the display is interlaced, the scan line increments
;**   must also be updated.
;**
;*/

cbts4:  mov     ecx,[yBuff]             ;get y co-ordinate
        or      ecx,ecx                 ;is it on the screen?
        jge     short cbts8             ;if greater than zero no top clipping

;/*
;**
;**   Pointer clips top of screen
;**
;*/

        neg     ecx                     ;get number of scan lines of clipping
        push    ecx
        align   4                                                       ;          
cbts6:  add     esi,[nbLine]            ;ESI == ESI + ECX * (width of buffer)
        cmp     [ebx].offOddBank,0
        je      short cbts7
        add     edi,[ebx].offOddBank    ;handle CGA-style bank offset here
        cmp     edi,[pInvalid]
        jb      short cbts7a
        sub     edi,[ebx].offOddBank
        sub     edi,[ebx].offOddBank

cbts7:  add     edi,eax                 ;jump to next scan line
        add     edi,edx                 ;skip forward width of buffer
cbts7a: hploop  cbts6                   ;continue for each clipping line

        pop     ecx                     ;ECX == amount clipped
        jmp     short cbts9

;/*
;**
;**     Check the bottom of the image to see if clipping must be done.
;**     If it must be done, then clip by adjusting the scan line count.
;**
;*/

cbts8:  add     ecx,[height]            ;add buffer/pointer height to scan line
                                        ;subtract max scan line number
        sub     ecx,[ebx].PtrData.vmssVideo.vmss_ulHeight
        jle     short cbts10            ;branch if no bottom clipping

cbts9:  sub     [height],ecx            ;adjust buffer/pointer height for # of
                                        ; lines to be clipped
        jle     short cbts14            ;branch if nothing on screen

;/*
;**
;**   Now copy the clipped region of the buffer/pointer to the screen:
;**
;**     EAX    == next scan line increment
;**     EDX    == width of move in bytes
;**     height == # of scan lines to copy
;**     EDI    == screen destination starting offset
;**     ESI    == screen buffer source starting offset
;**
;*/

cbts10: mov     dh,byte ptr [height]    ;DH == # of scan lines to display

cbts11: sub     ecx,ecx                 ;clear high bits of width
        mov     cl,dl                   ;CL == width for one scan line
        rep     movsb                   ;copy a scan line
        add     edi,eax                 ;point to next display scan line
        add     esi,[nbxClip]           ;point to next buffer/pointer data
        mov     ecx,[ebx].offOddBank
        jecxz   short cbts12
        sub     edi,[ebx].nbVRAMRow     ;handle CGA-style bank offset here
        add     edi,ecx
        cmp     edi,[pInvalid]
        jb      short cbts12
        sub     edi,ecx
        sub     edi,ecx
        add     edi,[ebx].nbVRAMRow
cbts12: dec     dh                      ;decrement scan line counter
        jnz     short cbts11            ;branch if more scan lines to display

cbts14:
        ExitProc
EndProc   vvPtrCopyBufferToScreen


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrGetOffsetIntoScrSave
;*
;* DESCRIPTION   = The given screen (x,y) coordinate is mapped into a pointer into the
;*                 screen-save buffer.
;*
;* INPUT         = EAX == x coordinate
;*                 ESI == y coordinate
;*                 EBX -> VDM instance data
;*
;* OUTPUT        = ESI == Pointer into screen-save buffer
;*                 ECX == Default pointer height
;*                 EAX == Difference between buffer width and pointer width
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrGetOffsetIntoScrSave

;/*
;**
;**   Compute the byte difference between the screen location of the left side
;**   of the screen buffer and the screen location of the left side of the pointer
;**
;*/

        sub     eax,[ebx].PtrData.xScrBuff      ;EAX == bit difference
        mov     ecx,BYTESCRSAVWD
        mov     edx,BYTESCRSAVWD-BYTEPTRSAVWD

        test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jnz     short mapxy             ;byte-pixel mode
        sar     eax,3                   ;divide by 8 to get byte difference
        mov     ecx,SCRSAVWD
        mov     edx,SCRSAVWD-PTRSAVWD

;/*
;**
;**   Compute byte difference between the screen location of the top scan line of
;**   the screen buffer & the screen location of the top scan line of the pointer
;**   and add it to the byte difference
;**
;*/

mapxy:  xchg    eax,esi                 ;ESI == byte difference, EAX == line
        sub     eax,[ebx].PtrData.yScrBuff      ;EAX == scan line difference
        push    edx
        imul    ecx
        pop     edx
        add     esi,eax                 ;ESI == byte offset
        add     esi,[ebx].PtrData.offScrSave    ;ESI == offset into pointer buffer
        mov     ecx,PTRSAVHT            ;ECX == pointer height
        mov     eax,edx                 ;EAX == difference between buffer width
                                        ; and pointer width
        ExitProc
EndProc   vvPtrGetOffsetIntoScrSave


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrGetScrBuffOffset
;*
;* DESCRIPTION   = Get screen buffer offset
;*
;* INPUT         = EBX -> VDM instance data
;*
;* OUTPUT        = PtrData.xScrBuff, PtrData.yScrBuff and PtrData.offScrBuff
;*                 initialized EDX == 0 if pointer moved a little, 1 if
;*                 moved a lot
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrGetScrBuffOffset
        sub     edx,edx                 ;assume little pointer movement
        test    [ebx].PtrData.flPtrVideo,PTR_PTRSAVE
        jz      short fm3

        mov     eax,[ebx].PtrData.xCorner
        mov     ecx,[ebx].PtrData.xCornerPrev   ;put min in ECX
        cmp     eax,ecx
        jge     short fm1
        xchg    eax,ecx                 ;ECX = minimum

fm1:    test    [ebx].PtrData.flPtrVideo,PTR_BYTEPIXEL
        jz      short fm1a              ;branch if not byte/pixel mode
        sub     eax,ecx
        cmp     eax,(SCRSAVWD-PTRSAVWD)*8
        jmp     short fm1b

fm1a:   and     ecx,NOT (8-1)
        sub     eax,ecx
        cmp     eax,PTRSAVWD*8
fm1b:   jge     short fm3               ;jumped too far
        mov     [ebx].PtrData.xScrBuff,ecx

        mov     eax,[ebx].PtrData.yCorner
        mov     ecx,[ebx].PtrData.yCornerPrev   ;put min in ECX
        sub     eax,ecx
        jge     short fm2
        neg     eax
        mov     ecx,[ebx].PtrData.yCorner       ;put min in ECX
fm2:    cmp     eax,SCRSAVHT-PTRSAVHT
        jle     short fm4

fm3:    mov     eax,[ebx].PtrData.xCorner
        and     eax,NOT (8-1)
        mov     [ebx].PtrData.xScrBuff,eax
        mov     eax,[ebx].PtrData.yCorner
        mov     [ebx].PtrData.yScrBuff,eax
        sub     eax,eax
        inc     edx                     ;pointer moved a lot
        jmp     short fm5

fm4:    mov     [ebx].PtrData.yScrBuff,ecx
fm5:    add     eax,PTRSAVHT
        mov     [ebx].PtrData.htScrBuff,eax
        mov     eax,[ebx].PtrData.xScrBuff
        mov     esi,[ebx].PtrData.yScrBuff
        call    vvPtrGetScreenOffset
        mov     [ebx].PtrData.offScrBuff,esi
        ExitProc
EndProc   vvPtrGetScrBuffOffset


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrGetScreenOffset
;*
;* DESCRIPTION   = Compute screen address of point (EAX,ESI)
;*
;* INPUT         = EAX == x coordinate
;*                 ESI == y coordinate
;*                 EBX -> VDM instance data
;*
;* OUTPUT        = ESI == corresponding screen offset
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrGetScreenOffset
        push    edx
        push    edi
        xchg    eax,esi                 ;ESI == x, EAX == y

        mov     edi,[ebx].offOddBank
        or      edi,edi
        jz      short gso1
        sar     eax,1                   ;drop the "bank bit" from y
        jc      short gso1
        sub     edi,edi                 ;bank bit was clear, so zero offset

gso1:   imul    [ebx].nbVRAMRow
        xchg    eax,esi                 ;EAX == x, ESI == y (again)
        cmp     [ebx].nPixelsByte,4
        jb      short gso2
;       div     [ebx].nPixelsByte       ;divide x by pixels per byte
        sar     eax,3                   ;(shift isn't general, but it's all we need)

gso2:   add     esi,eax
        add     esi,edi                 ;ESI == screen offset

        pop     edi
        pop     edx
        ExitProc
EndProc   vvPtrGetScreenOffset


;/*************************************************************************
;*
;* FUNCTION NAME = vvPtrEnableReadMap
;*
;* DESCRIPTION   = Enable read map
;*
;* INPUT         = AL  == map # to enable
;*                 EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrEnableReadMap
        mov     ah,al                   ;plane # to enable
        mov     al,REG_GDCREADMAP       ;register #
        mov     edx,PORT_GDCINDX        ;select graphics controller
        OUTW    dx,ax                   ;do it!
        ExitProc
EndProc   vvPtrEnableReadMap


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrEnableWriteMaps
;*
;* DESCRIPTION   = Enable write maps
;*
;* INPUT         = AL  == maps to enable
;*                 EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrEnableWriteMaps
        mov     ah,al                   ;planes to enable
        mov     al,REG_SEQMAPMASK       ;register #
        mov     edx,PORT_SEQINDX        ;select sequencer port
        OUTW    dx,ax                   ;do it!
        ExitProc
EndProc   vvPtrEnableWriteMaps


       IFDEF EGAVGA

;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrSetState
;*
;* DESCRIPTION   = Save (if necessary) and set video stat
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrSetState
        cmp     [ebx].PtrData.iPtrMode,PTRMODE_NOPLANE
        je      short setstate_exit     ;no state save/restore needed

       IFDEF VGA                        ;on a VGA, update all untrapped regs
        test    [ebx].flVDMVideo,VDM_IOUPDATED
        jnz     short ss1
        CallFn  vvGetIOState,<[ebx].hvdmVideo, <FLAToffset aprlePtrState>> ;/*          */
ss1:
       ENDIF

        CallFn  vvSaveLatches,<[ebx].hvdmVideo, TRUE>

;/*
;**
;**   Set all essential hardware registers to appropriate state
;**
;*/

       IFDEF EGAVGA
        CallFn  vvSetIOState,<[ebx].hvdmVideo, <FLAToffset aprlePtrState>, [ebx].PtrData.iPtrMode> ;/*          */
       ENDIF

setstate_exit:
        ExitProc
EndProc   vvPtrSetState


;/***************************************************************************
;*
;* FUNCTION NAME = vvPtrRestoreState
;*
;* DESCRIPTION   = Restore video state if it was changed
;*
;* INPUT         = EBX -> VDM instance data
;*                 EDI -> VRAM alias
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        align   4                                                       ;          
Procedure vvPtrRestoreState
        cmp     [ebx].PtrData.iPtrMode,PTRMODE_NOPLANE
        je      short rs9               ;no state save/restore needed

        CallFn  vvRestoreLatches,<[ebx].hvdmVideo, TRUE>

;/*
;**
;**   Restore all modified hardware registers to original state
;**
;*/

       IFDEF EGAVGA
        CallFn  vvSetIOState,<[ebx].hvdmVideo, <FLAToffset aprlePtrState>, PTRMODE_NOPLANE> ;/*          */
       ENDIF

rs9:    ExitProc
EndProc   vvPtrRestoreState

       ENDIF  ;EGAVGA


        EndCode     PRIVATE,SWAP,PASCAL


        END
