;*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     55,132
        TITLE    Pointer Drawing Functions
        SUBTITLE Header
;/*****************************************************************************
;*
;* SOURCE FILE NAME = POINTER.ASM
;*
;* DESCRIPTIVE NAME = Pointer shape routines
;*
;*
;* VERSION      V2.0
;*
;* DATE
;*
;* DESCRIPTION
;*
;*  This file contains the pointer shape routines required to draw the
;*  pointer shape on the VGA.
;*
;*  All display drivers must support a "pointer" for the pointing device.
;*  The pointer is a small graphics image which is allowed to move around
;*  the screen independantly of all other operations to the screen, and is
;*  normally bound to the location of the pointing device.  The pointer is
;*  non-destructive in nature, i.e.; the bits underneath the pointer image
;*  are not destroyed by the presence of the pointer image.
;*
;*  A pointer consists of an AND mask and an XOR mask, which give
;*  combinations of 0's, 1's, display, or inverse display.
;*
;*                   AND XOR | DISPLAY
;*                   ----------------------
;*                    0   0  |    0
;*                    0   1  |    1
;*                    1   0  |   Display
;*                    1   1  | Not Display
;*
;*  The pointer also has a "hot spot", which is the pixel of the pointer
;*  image which is to be aligned with the actual pointing device location.
;*
;*
;*                 |            For a pointer like this, the hot spot
;*                 |            would normally be the *, which would
;*              ---*---         be aligned with the pointing device
;*                 |            position
;*                 |
;*
;*  The pointer may be moved to any location on the screen, be restricted to
;*  only a section of the screen, or made invisible.  Part of the pointer
;*  may actually be off the edge of the screen, and in such a case only the
;*  visible portion of the pointer image is displayed.
;*
;*  Logically, the pointer image isn't part of the physical display surface.
;*  When a drawing operation coincides with the pointer image, the result is
;*  the same as if the pointer image wasn't there.  In reality, if the
;*  pointer image is part of the display surface it must be removed from
;*  memory before the drawing operation may occur, and redrawn at a later
;*  time.
;*
;*  This exclusion of the pointer image is the responsibility of the display
;*  driver.  If the pointer image is part of physical display memory, then
;*  all output operations must perform a hit test to determine if the
;*  pointer must be removed from display memory, and set a protection
;*  rectangle wherein the pointer must not be displayed.  The actual pointer
;*  image drawing routine must honor this protection rectangle by never
;*  drawing the pointer image within its boundary.
;*
;*  This code doesn't distinguish between pointers and icons, they both are
;*  the same size, 32 x 32, which comes out square.
;*
;* Restrictions:
;*
;*  All routines herein assume protection either via CLI/STI or a semaphore
;*  at higher level code.
;*
;*
;* FUNCTIONS    pointer_off
;*              draw_pointer
;*              ComputeRects
;*              ClipRects
;*              DoClipping
;*              XORToScreen
;*              lhs_XORToScreen
;*              rhs_XORToScreen
;*              _XORToScreen
;*              XORToScreen
;*              ColorPointerToScreen
;*              CPSDoAPass
;*              ANDIntoWork
;*              ANDFromScreen
;*              ANDFromSave
;*              CopyThingsAround
;*              CopySaveToScreen
;*              CopyScreenToSave
;*              save_hw_regs
;*              res_hw_regs
;*              GetEIPinEAX
;*              GetEIPinEBX
;*              GetEIPinEDX
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/04/93              55847  Save GRAF_ADDR index in save_hw_regs and
;*                                restore in restore_hw_regs ( this also
;*                                requires a fix in VVID, being made under
;*                                seperate defect # ).
;*   04/13/93              63750  Avoid video corruption on GDC Misc restore
;*
;*****************************************************************************/

        .386P
        .MODEL FLAT,SYSCALL

;/*
;**  Include files
;*/

INCL_GPIPRIMITIVES EQU 1
INCL_NOBASEAPI     EQU 1
INCL_SAADEFS       EQU 1
OS2_NOPMAPI        EQU 1
        INCLUDE PMGRE.INC
DINCL_BITMAP EQU 1
DINCL_ENABLE EQU 1
        INCLUDE DRIVER.INC
        INCLUDE EXTERN.INC
        INCLUDE PROTOS.INC

        INCLUDE EGAFAM.INC
        INCLUDE POINTER.INC
        INCLUDE ASSERT.MAC
        INCLUDE CURSORS.INC
        INCLUDE CURSTRC.INC
if SCAN_CNT EQ 768
        include oem_macs.inc
ECHO MOVE THIS SCREEN_LPB EQU 512 SOMETIME!!!
SCREEN_LPB EQU 512
GetEIPinEAX          PROTO SYSCALL
GetEIPinEBX          PROTO SYSCALL
GetEIPinEDX          PROTO SYSCALL
endif

;/*
;** Structures
;*/
        .ERRNZ  sizeof POINTS AND 1
        .ERRNZ  sizeof RECTS AND 1

;/*
;** Prototypes for local functions
;*/

ANDFromSave          PROTO SYSCALL
ANDFromScreen        PROTO SYSCALL
ANDIntoWork          PROTO SYSCALL
ColorPointerToScreen PROTO SYSCALL
CopySaveToScreen     PROTO SYSCALL
CopyScreenToSave     PROTO SYSCALL
CopyThingsAround     PROTO SYSCALL
CPSDoAPass           PROTO SYSCALL
DoClipping           PROTO SYSCALL
XORToScreen          PROTO SYSCALL
ComputeRects         PROTO SYSCALL
ClipRects            PROTO SYSCALL

;/*
;** Data
;*/

EXTERNDEF CurData:CURSORSTRUCT

_PtrCode32 SEGMENT DWORD PUBLIC FLAT 'CODE'
        SUBTITLE pointer_off
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = pointer_off
;*
;* DESCRIPTION   = The old pointer is removed from the screen if it currently
;*                 is on the screen.  A flag is set showing that the pointer
;*                 is no longer visible.  This function works by building a
;*                 call frame for draw_pointer that would place the cursor
;*                 off-screen, thereby causing it to be clipped.
;*               WARNING!
;*                 draw_pointer must immediately follow this function, as
;*                 the flow will continue directly through the end of the
;*                 function, there being no RE instruction.
;*
;*                 Registers Preserved:
;*                       per draw_pointer
;*                 Registers Destroyed:
;*                       per draw_pointer
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  EPILOGUE:NONE
        OPTION  PROLOGUE:NONE

;/*
;** pCurData in EDI
;** saves/restores EDI via draw_pointer
;*/

ALIGN 4
pointer_off PROC SYSCALL PUBLIC,
                ptlX:DWORD,
                ptlY:DWORD

        mov     DWORD PTR [ESP+4],SCREEN_CX       ;ptlX
        .ERRNZ  draw_pointer-$,<draw_pointer not contiguous with pointer_off>

pointer_off ENDP

        OPTION  EPILOGUE:EPILOGUEDEF
        OPTION  PROLOGUE:PROLOGUEDEF

        SUBTITLE draw_pointer
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = draw_pointer
;*
;* DESCRIPTION   = The pointer shape is drawn on the screen at the given
;*                 co-ordinates.  If it is currently displayed on the screen,
;*                 it will be removed from the old location first.
;*
;*                 Registers Preserved:
;*                       BP,DS
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,ES,FLAGS
;*
;* INPUT         = C Prototype:
;*                   void draw_pointer (LONG ptlX, LONG ptlY);
;*
;*                    where:
;*                      ptlX is the X location of the hot spot.
;*                      pltY is the Y location of the hot spot.NONE
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** pCurData in EDI
;** saves/restores EDI
;** makes calls
;*/

ALIGN 4
draw_pointer PROC SYSCALL PUBLIC uses EBX EDI EDX ESI,
                ptlX:DWORD,
                ptlY:DWORD

mov     ecx,edi
ASSUME  ECX:PCURSORSTRUCT

if SCAN_CNT EQ 768

;/*
;** This PUSHES the banks
;*/

        SaveBanks
endif
        PUSH    EBP                           ; DO NOT AUTOSAVE THIS!
        CLD

;/*
;** Set up the pointer to VRAM
;*/

        mov     edi,[ecx].pVRAMRing0
        MOV     AX,CS
        AND     AX,011b
        JE      @F
        mov     edi,pVRAMInstance             ;invalid at ring0
                                              ;required at ring3
@@:
        mov     [ecx].selScreenPtr,edi
        LEA     EDI,[ecx].pdPtr0              ; EDI --> new pointer's data
        MOV     ESI,EDI
        ADD     EDI,[ecx].ppdNew
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data

        MOV     EAX,ptlX                      ; Find index into mask pointer table
        MOV     EBX,EAX                       ; based on low 3 bits of x coordinate
        AND     EBX,00000111b
        SHL     EBX,2

        LEA     EDX,[ecx].BaseAndMasks
        ADD     EDX,[ecx].pabAndMasks[EBX]    ; EDX --> And mask for rotation

        MOV     [ecx].pAndXor,EDX

        LEA     EDX,[ecx].BaseColorMasks
        ADD     EDX,[ecx].pabClrMasks[EBX]    ; EDX --> Color mask for rotation

        MOV     [ecx].pColor,EDX

        MOV     EBX,PD_VALID            ; Assume visible
        SAR     EAX,3                   ; Compute starting byte address (set
                                        ; 'S') (Column co-ord / 8)
        MOV     [EDI].POINTER_DATA.rd.ptlScreen.ptl_x,EAX

;/*
;** Compute any X clipping parameters for the new pointer image.
;*/

        JS      Clip_LHS_of_pointer              ; If X is neg, LHS clipping needed
        SUB     EAX,[ecx].ptlBotRightClip.ptl_x
        JLE     Done_X_Clipping
        MOV     BH,PD_CLIP_RIGHT        ; AX = amount to clip off RHS
        JMP     Finish_X_clip

Clip_LHS_of_pointer:
        NEG     EAX                     ; Want |ax|
        MOV     BH,PD_CLIP_LEFT

Finish_X_clip:
        CMP     AX,[ecx].sizsMask.sizs_cx        ; Width of pointer in bytes
        JGE     Not_visible                              ; Clipped away too much
        OR      BL,BH

Done_X_Clipping:

;/*
;** Compute any Y clipping parameters for the new pointer image.
;*/

        MOV     EAX,ptlY
        MOV     [EDI].POINTER_DATA.rd.ptlScreen.ptl_y,EAX
        OR      EAX,EAX
        JS      Clip_top_of_pointer              ; If Y is neg., top clipping needed
        SUB     EAX,[ecx].ptlBotRightClip.ptl_y
        JLE     Done_Y_clipping
        MOV     BH,PD_CLIP_BOTTOM       ; AX = amount to clip off bottom
        JMP     Finish_Y_clip

;/*
;** Not_visible - the pointer will be totally off the screen.  All we have to
;** do is to determine if any part of the old pointer is visible and remove it
;** if so.
;*/

Not_visible:
        TEST    [ESI].POINTER_DATA.fb,PD_VALID
        JZ      Draw_pointer_exit                        ; No new, no old
        XOR     EAX,EAX
        MOV     [EDI].POINTER_DATA.fb,AL ;Clear PD_VALID flag, clipping flags
        MOV     WORD ptr [ecx].fbAndRead,AX   ; Nothing to read/AND/XOR
        .ERRNZ  CURSORSTRUCT.fbXor-CURSORSTRUCT.fbAndRead-1
        MOV     [ecx].fbFlush,FB_OLD_PTR      ; Write old to screen
        JMP     Rectangles_been_computed

;/*
;** Continue with Y clipping
;*/

Clip_top_of_pointer:
        NEG     EAX                     ; Want |AX|
        MOV     BH,PD_CLIP_TOP

Finish_Y_clip:
        CMP     AX,[ecx].sizsMask.sizs_cy                ; Height of pointer in scans
        JGE     Not_visible                              ; Clipped away too much
        OR      BL,BH

Done_Y_clipping:
        MOV     [EDI].POINTER_DATA.fb,BL ; Set clipping flags and show valid

;/*
;** It looks like some portion of the pointer image will be visible.
;** Initialize some of the new pointer's POINTER_DATA structure.
;*/

        MOV     AX,[ecx].sizbMask             ; ptbSave will be set by ComputeRects
        MOV     [EDI].POINTER_DATA.rd.sizb,AX
        .ERRNZ  (sizeof SIZEB) - 2
        XOR     EAX,EAX
        MOV     [EDI].POINTER_DATA.rd.ptbWork,AX
        .ERRNZ  (sizeof POINTB) - 2

;/*
;** Compute the rectangles describing how things overlap and then clip them.
;** WARNING: NO FRAME VARIABLES MAY BE ACCESSED BEYOND THIS POINT
;*/


;/*
;** MyPtr is ecx
;*/

        push    ecx
        INVOKE  ComputeRects
        POP     ECX
Rectangles_been_computed:
        push    ecx
        INVOKE  ClipRects
        MOV     ECX,DWORD PTR [ESP]
        INVOKE  save_hw_regs                             ; Save EGA registers
        POP     ECX

        TEST    [ecx].fbPointer,FBPTR_COLOR      ; Color pointer?
        JNZ     Draw_color_pointer

;/*
;**                        Draw B/W Pointer
;*/

;/*
;** AND from the save area and the screen into the work area.
;*/

        MOV     AL,[ecx].fbAndRead
        OR      AL,AL
        JZ      Done_AND_portion
        PUSH    ECX
        INVOKE  ANDIntoWork
        POP     ECX

Done_AND_portion:

;/*
;** Copy from save area to the screen and from the screen to the save area
;*/

        MOV     AX,WORD ptr [ecx].fbFlush     ; Assume nothing to copy to/from save
        .ERRNZ  CURSORSTRUCT.fbAndRead-CURSORSTRUCT.fbFlush-1
        OR      AH,AL
        JZ      Copied_things_around
        PUSH    ECX
        INVOKE  CopyThingsAround
        POP     ECX

Copied_things_around:

;/*
;** XOR from the work area to the screen.
;*/

        MOV     AL,[ecx].fbXor
        OR      AL,AL
        JZ      Pointer_drawn
        push    ecx
        INVOKE  XORToScreen
        pop     ecx
        JMP     Pointer_drawn

;/*
;** Draw Color Pointer
;*/

Draw_color_pointer:

;/*
;** Copy from save area to the screen and from the screen to the save area
;*/

        MOV     AX,WORD ptr [ecx].fbFlush     ; Assume nothing to copy to/from
                                              ; save
        .ERRNZ  CURSORSTRUCT.fbAndRead-CURSORSTRUCT.fbFlush-1
        OR      AH,AL
        JZ      Things_copied_around
        push    ecx
        INVOKE  CopyThingsAround
        pop     ecx
Things_copied_around:

;/*
;** Draw color pointer to screen
;*/

        TEST    [ecx].fbXor,0FFh
        JZ      Pointer_drawn
        ;ColorPointerToScreen saves/restores ecx
        INVOKE  ColorPointerToScreen             ; Planes must all be enabled

Pointer_drawn:
        push    ecx
        INVOKE  res_hw_regs                      ; Restore user's state
        pop     ecx

        MOV     EAX,[ecx].ppdNew
        XCHG    EAX,[ecx].ppdOld
        MOV     [ecx].ppdNew,EAX

Draw_pointer_exit:
        POP     EBP                              ; DO NOT AUTOSAVE THIS
if SCAN_CNT EQ 768

;/*
;** This POPS the banks
;*/

       RestoreBanks

endif
        RET

draw_pointer ENDP

        SUBTITLE ComputeRects
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ComputeRects
;*
;* DESCRIPTION   = This routine computes the rectangles which describe what
;*                 needs to be read/ANDed/XORed/written.  The rectangles are
;*                 unclipped.  Clipping must be performed by a different
;*                 routine.
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = AX  =  0
;*                 SI --> Currently displayed pointer's rectangle data
;*                 DI --> New pointer's rectangle data
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
;/*
;** ECX is passed in as ptr to curdata
;** No need to save/restore ecx
;** Makes no calls
;*/

ALIGN 4
ComputeRects PROC SYSCALL PUBLIC
        MOV     WORD ptr [ecx].fbFlush,AX     ; Assume nothing to restore to screen
        .ERRNZ  CURSORSTRUCT.fbAndRead-CURSORSTRUCT.fbFlush-1     ; Assume nothing to read/And to work
        MOV     [ecx].fbXor,FB_NEW_PTR        ; Assume new pointer is XORed to scrn
        TEST    [ESI].POINTER_DATA.fb,PD_VALID
        JZ      Old_pointer_is_invalid

;/*
;** There is a pointer currently displayed on the screen.  If the new pointer
;** is far enough away from the old pointer, then we won't have to deal with
;** overlap.
;*/

Old_pointer_is_valid:
        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_x
        SUB     EAX,[ESI].POINTER_DATA.rd.ptlScreen.ptl_x
        MOV     BL,AL                   ; BL = delta x

;/*
;**  ABS EAX
;*/

        CDQ
        XOR     EAX,EDX
        SUB     EAX,EDX
;**
        CMP     AX,[ecx].sizsMaxDelta.sizs_cx
        JAE     Rects_are_disjoint
        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_y
        SUB     EAX,[ESI].POINTER_DATA.rd.ptlScreen.ptl_y
        MOV     BH,AL                   ; BH = delta y

;/*
;**  ABS EAX
;*/
        CDQ
        XOR     EAX,EDX
        SUB     EAX,EDX
;**
        CMP     AX,[ecx].sizsMaxDelta.sizs_cy
        JB      Rects_overlap                            ; (or are identical)

;/*
;** The rectangles will be disjoint.  Set up to restore under the old pointer
;** copy the new rectangle to the save area, and AND it into the work area.
;*/

Rects_are_disjoint:
        MOV     [ecx].fbFlush,FB_OLD_PTR

;/*
;** The save area image is invalid, so we won't have to copy it to the screen
;** or AND some part of it into the work area.  We'll simply want to copy the
;** new area to the save area and AND it into the work area.  This can be
;** treated the same as if the rectangles are identical, except we want to
;** reset the origin within the save buffer.
;*/

Old_pointer_is_invalid:
        MOVZX   EAX,[ecx].ptbInitOrigin        ; Reset origin within the save area
        MOV     [EDI].POINTER_DATA.rd.ptbSave,AX
        .ERRNZ  (sizeof POINTER_DATA.rd.ptbSave) - 2
        MOV     [ecx].fbAndRead,FB_NEW_PTR    ; Copy new ptr to save and XOR to work
        JMP     ComputeRects_exit

;/*
;** The rectangles overlap in some manner.  Compute how the rectangles, will
;** overlap, setting up the various needed rectangle structures as we go.
;**
;** The only hope we have of computing the overlap rectangle is to initialize
;** it to some known state and adjusting it as we process dx and dy.  We will
;** initialize it to be the upper left hand corner of the old pointer
;** rectangle.
;**
;** Currently:
;**        AX  =   old pointer's rd.ptbSave
;**        BH  =   dy
;**        BL  =   dx (negative)
;**        ESI --> old pointer's ptlScreen
;**        EDI --> new pointer rectangle
;*/

Rects_overlap:

;/*
;** Set old pointer's save buffer (X,Y) into the overlap rectangle.  Also set
;** this as the save buffer origin for the new pointer rectangle.
;*/

        XOR     EAX,EAX
        LODSW
        .ERRNZ  POINTER_DATA.rd.ptbSave
        MOV     [ecx].rdOverlap.ptbSave,AX
        MOV     [EDI].POINTER_DATA.rd.ptbSave,AX
        XCHG    EAX,EDX                 ; DX = Old ptr's save x & y
;/*
;** Set old pointer's screen (X,Y) into the overlap rectangle as the screen
;** origin.
;*/

        LODSD
        .ERRNZ  RECT_DATA.ptlScreen-RECT_DATA.ptbSave-2
        .ERRNZ  POINTL.ptl_x
        MOV     [ecx].rdOverlap.ptlScreen.ptl_x,EAX
        XCHG    EAX,EBP                 ; EBP = Old ptr's screen X
        LODSD
        .ERRNZ  POINTL.ptl_y-POINTL.ptl_x-4
        MOV     [ecx].rdOverlap.ptlScreen.ptl_y,EAX
        XCHG    EAX,ESI                 ; ESI = Old ptr's screen Y

;/*
;** Set the mask width and height into the overlap rectangle.
;*/

        MOVZX   EAX,[ecx].sizbMask
        MOV     [ecx].rdOverlap.sizb,AX
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        .ERRNZ  (sizeof SIZEB) - 2
        MOV     [ecx].SaveESI,ESI
        mov     ESI,ECX
ASSUME  ESI:PCURSORSTRUCT

        XCHG    EAX,ECX                 ; AX = Mask width & height
ASSUME  ECX:NOTHING
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
;/*
;** Set the work buffer origin to be zero
;*/

        xor     EAX,EAX
        MOV     [esi].rdOverlap.ptbWork,AX

;/*
;** Show that the overlap rectangle exists and should be ANDed into the work
;** area, then dispatch based on dx,dy.
;*/

        MOV     [esi].fbAndRead,FB_OVERLAP
        OR      BL,BL                   ; Dispatch based on DX
        JG      Moved_right
        JL      Moved_left
        JMP     Processed_x_overlap

;/*
;** The starting X of the new rectangle is to be set as the new LHS.
;**
;**      * nnnnn $ onononono oooooooo        * = start of new rectangle
;**      n       n          o       o        $ = start of old rectangle
;**      n   |   o    |     n   |   o
;**      n   |   n          o   |   o
;**      n   |   o    O     n       o
;**      n       n    v     o   F   o
;**      n   R   o    e     n   l   o
;**      n   e   n    r     o   u   o
;**      n   a   o    l     n   s   o
;**      n   d   n    a     o   h   o
;**      n       o    p     n       o
;**      n   |   n          o   |   o
;**      n   |   o    |     n   |   o
;**      n   |   n    |     o   |   o
;**      n       o          n       o
;**      nnnnnnnn nonononono oooooooo
;**
;**      |-- DX -|          |-- DX -|
;**      |-sizbMask.sizb_cx-|
;**
;**
;** Currently:
;**        AX =        0
;**        BH =        dy
;**        BL =        dx (negative)
;**        CH =        buffer height
;**        CL =        buffer width
;**        DH =        old pointer's Y coordinate in save area
;**        DL =        old pointer's X coordinate in save area
;**        SI =        old pointer's Y screen coordinate
;**        BP =        old pointer's X screen coordinate
;**        DI =        --> new pointer's RECT_DATA
;*/

Moved_left:

;/*
;** The Read buffer will map into the work area at (0,0).
;*/

        MOV     [esi].rdReadX.ptbWork,ax

;/*
;** The width of the overlap area is sizbMask.sizb_cx - |dx|.
;*/

        MOV     AL,BL                  ; BL = dx (which is negative)
        ADD     AL,CL              ; CL = sizbMask.sizb_cx
        MOV     [esi].rdOverlap.sizb.sizb_cx,AL

;/*
;** The flush rectangle's X is ptlScreen.ptl_x + sizbMask.sizb_cx - |dx|.
;*/

        ADD     EAX,EBP                 ; AX = sizbMask.sizb_cx - |dx|
        MOV     [esi].rdFlushX.ptlScreen.ptl_x,EAX
;/*
;** Compute where in the save buffer the new LHS will map to.  We must
;** update the new pointer's rectangle to reflect where this origin is.
;*/

        MOV     EAX,EDX                 ; DX = old ptbSave
        ADD     AL,BL                   ; BL = dx (negative)
        ADD     AH,BH                   ; BH = dy
        AND     EAX,((SAVE_BUFFER_HEIGHT-1) SHL 8) + SAVE_BUFFER_WIDTH-1
        MOV     [EDI].POINTER_DATA.rd.ptbSave.ptb_x,AL
        MOV     [esi].rdReadX.ptbSave,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
;/*
;** The origin of the flush rectangle is sizbMask.sizb_cx bytes away
;** from the origin of the read rectangle.
;*/

        ADD     AL,CL
        AND     AL,SAVE_BUFFER_WIDTH-1  ; Handle any wrap
        MOV     AH,DH
        MOV     [esi].rdFlushX.ptbSave,AX
;/*
;** Compute |dx|.  This is the width of the read and flush rectangles.
;** The height will be set to the working height.  |dx| is also the
;** overlap rectangle's work area X coordinate.
;*/

        MOV     AL,BL                   ; BL = dx (negative)
        NEG     AL                      ; AL = |dx|
        MOV     AH,CH                   ; CH = sizbMask.sizb_cy
        MOV     [esi].rdFlushX.sizb,AX
        MOV     [esi].rdReadX.sizb,AX
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        MOV     [esi].rdOverlap.ptbWork.ptb_x,AL
;/*
;** The Read buffer's screen address is the ptlScreen stored in the new
;** pointer's RECT_DATA.
;*/

        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_x
        JMP     Finish_x_overlap

;/*
;**
;** rectangle.
;**
;**      $ ooooo * onononono nnnnnnn     * = start of new rectangle
;**
;**      o       n          o      n     $ = start of old rectangle
;**
;**      o   |   o    |     n   |  n
;**      o   |   n          o   |  n
;**      o       o    O     n   |  n
;**
;**      o   F   n    v     o      n
;**
;**      o   l   o    e     n   R  n
;**      o   u   n    r     o   e  n
;**      o   s   o    l     n   a  n
;**      o   h   n    a     o   d  n
;**      o       o    p     n      n
;**
;**      o   |   n          o   |  n
;**
;**      o   |   o    |     n   |  n
;**      o   |   n    |     o   |  n
;**      o       o          n      n
;**      oooooooo nonononono nnnnnnn
;**
;**      |-- DX -|          |-- DX -|
;**
;**      |-sizbMask.sizb_cx-|
;**
;**
;** Currently:
;**        AX =        0
;**        BH =        dy
;**        BL =        dx (positive)
;**        CH =        buffer height
;**        CL =        buffer width
;**        DH =        old pointer's Y coordinate in save area
;**        DL =        old pointer's X coordinate in save area
;**
;**        SI =        old pointer's Y screen coordinate
;**        BP =        old pointer's X screen coordinate
;**        DI =        --> new pointer's RECT_DATA
;*/


Moved_right:

;/*
;** The screen X origin of the overlap rectangle is the new rectangle's
;** X coordinate or the old rectangle's X coordinate + |dx|.
;*/

        MOV     AL,BL
        ADD     [esi].rdOverlap.ptlScreen.ptl_x,EAX

;/*
;** The width of the read and flush buffers is |dx|.  The height is just the
;** working height.
;*/

        MOV     AH,CH                   ; CH = sizbMask.sizb_cy
        MOV     [esi].rdFlushX.sizb,AX
        MOV     [esi].rdReadX.sizb,AX
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

;/*
;** Compute where the new LHS is in the save area.  This will be the LHS of
;** both the new rectangle and the overlap area.
;*/

        ADD     AL,DL                   ; DL = ptbSave.ptb_x
        AND     AL,SAVE_BUFFER_WIDTH-1  ; Handle any wrap
        MOV     [EDI].POINTER_DATA.rd.ptbSave.ptb_x,AL
        MOV     [esi].rdOverlap.ptbSave.ptb_x,AL

;/*
;** The data to be flushed will come from the LHS of the old rectangle
;*/

        MOV     EAX,EDX
        MOV     [esi].rdFlushX.ptbSave,AX

;/*
;** The data to be read will go at the old LHS + sizbMask.sizb_cx.  The
;** Y component will be the new Y.
;*/

        ADD     AL,CL
        ADD     AH,BH
        AND     EAX,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        MOV     [esi].rdReadX.ptbSave,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1

;/*
;** The X screen origin of the flush buffer is the old ptlScreen.ptl_x.
;*/

        MOV     [esi].rdFlushX.ptlScreen.ptl_x,EBP

;/*
;** The width of the overlap rectangle is sizbMask.sizb_cx - |dx|.  This is
;** also the X offset into the work area of the read rectangle.  The Y offset
;** is zero.
;*/

        MOV     AL,CL
        SUB     AL,BL
        MOV     [esi].rdOverlap.sizb.sizb_cx,AL
        MOVZX   EAX,AL
        MOV     [esi].rdReadX.ptbWork,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1

;/*
;** The screen Y origin of the read rectangle is the new rectangle's Y
;** coordinate.  The X coordinate can be computed as the old rectangle's
;** X coordinate + the save width.
;*/

        MOV     AL,CL
        ADD     EAX,EBP

Finish_x_overlap:
        MOV     [esi].rdReadX.ptlScreen.ptl_x,EAX
        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_y
        MOV     [esi].rdReadX.ptlScreen.ptl_y,EAX

;/*
;** The Y address of the flush rectangle on the screen is ptlScreen.ptl_y.
;*/

        MOV     EAX,[ESI].SaveESI
        MOV     [esi].rdFlushX.ptlScreen.ptl_y,EAX
;/*
;** Set the flags to show that there is some form of X overlap.  We want to
;** show that there is some X rectangle to be read/flushed, and that there is
;** some overlap rectangle to be processed.
;*/


        OR      WORD ptr [esi].fbFlush,(FB_READ_X shl 8) + FB_FLUSH_X
        .ERRNZ  CURSORSTRUCT.fbAndRead-CURSORSTRUCT.fbFlush-1
        XOR     EAX,EAX

Processed_x_overlap:

        OR      BH,BH
        JG      Moved_down
        JZ      Processed_y_overlap_relay

;/*
;**
;** The starting Y of the new rectangle is to be set as the new top.
;**
;**
;**   * = start of new rectangle
;**   $ = start of old rectangle
;**
;**
;**
;**      $ oooooooooooooooooooooooooo   ---  ---
;**      o                          o    |    |
;**      o -------- Read ---------- o    dy   |
;**      o                          o    |    |
;**      * onononononononononononono    ---
;**
;**      o                          n        sizbMask.sizb_cy - dy
;**
;**      n                          o
;**      o ------- Overlap -------- n         |
;**      n                          o         |
;**      o                          n         |
;**       ononononononononononononon   ---  ---
;**      n                          n    |
;**
;**      n -------- Write --------- n    dy
;**      n                          n    |
;**      nnnnnnnnnnnnnnnnnnnnnnnnnnnn   ---
;**
;**
;** Currently:
;**        AX =        0
;**        BH =        dy (negative)
;**        BL =        DX
;**        CH =        buffer height
;**        CL =        buffer width
;**
;**        DH =        old pointer's Y coordinate in save area
;**        DL =        old pointer's X coordinate in save area
;**        SI =        old pointer's Y screen coordinate
;**        BP =        old pointer's X screen coordinate
;**        DI =        --> new pointer's RECT_DATA
;*/

Moved_up:

;/*
;** The Read buffer will map into the work area at (0,0).
;*/

        MOV     [esi].rdReadY.ptbWork,AX

;/*
;** The height of the overlap area is sizbMask.sizb_cy - |dy|.
;*/

        MOV     AL,BH                   ; BH = dy (which is negative)
        ADD     AL,CH                   ; CH = sizbMask.sizb_cy
        MOV     [esi].rdOverlap.sizb.sizb_cy,AL

;/*
;** The flush rectangle's Y is ptlScreen.ptl_y + sizbMask.sizb_cy - |dy|.
;*/

        ADD     EAX,[ESI].SaveESI      ; EAX = sizbMask.sizb_cy - |dy|
        MOV     [esi].rdFlushY.ptlScreen.ptl_y,EAX
;/*
;** Compute where in the save buffer the new top will map to.  We must update
;** the new pointer's rectangle to reflect where this origin is.
;*/

        MOV     EAX,EDX                   ; DX = old ptbSave
        ADD     AH,BH                   ; BH = dy (negative)
        ADD     AL,BL
        AND     EAX,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        MOV     [EDI].POINTER_DATA.rd.ptbSave.ptb_y,AH
        MOV     [esi].rdReadY.ptbSave,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
;/*
;** The origin of the flush rectangle is sizbMask.sizb_cy scans away from the
;** origin of the read rectangle.
;*/

        ADD     AH,CH
        AND     AH,SAVE_BUFFER_HEIGHT-1 ; Handle any wrap
        MOV     AL,DL
        MOV     [esi].rdFlushY.ptbSave,AX
;/*
;** Compute |dy|.  This is the height of the read and flush rectangles.
;** The width will be set to the working width.  |dy| is also the
;** overlap rectangle's work area Y coordinate.
;*/

        MOV     AH,BH                   ; BH = dy
        NEG     AH                      ; Make it |dy|
        MOV     AL,CL                   ; CL = sizbMask.sizb_cx
        MOV     [esi].rdFlushY.sizb,AX
        MOV     [esi].rdReadY.sizb,AX
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        MOV     [esi].rdOverlap.ptbWork.ptb_y,AH
;/*
;** The Read buffer's screen address is the ptlScreen stored in the new
;** pointer's RECT_DATA.
;*/

        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_y
        JMP     Finish_y_overlap

Processed_y_overlap_relay:
        JMP     Processed_y_overlap

;/*
;** The starting Y of the new rectangle is somewhere in the middle of the old
;** rectangle.
;**
;**   * = start of new rectangle
;**   $ = start of old rectangle
;**
;**      $ oooooooooooooooooooooooooo   ---  ---
;**      o                          o    |    |
;**      o -------- Write --------- o    dy   |
;**      o                          o    |    |
;**      * onononononononononononono    ---
;**      o                          n        sizbMask.sizb_cy - dy
;**      n                          o
;**      o ------- Overlap -------- n         |
;**      n                          o         |
;**      o                          n         |
;**       ononononononononononononon    ---  ---
;**      n                          n    |
;**      n -------- Read ---------- n    dy
;**      n                          n    |
;**      nnnnnnnnnnnnnnnnnnnnnnnnnnnn   ---
;**
;** Currently:
;**        AX =        0
;**        BH =        dy (positive)
;**        BL =        DX
;**        CH =        buffer height
;**        CL =        buffer width
;**        DH =        old pointer's Y coordinate in save area
;**        DL =        old pointer's X coordinate in save area
;**        SI =        old pointer's Y screen coordinate
;**        BP =        old pointer's X screen coordinate
;**        DI =        --> new pointer's RECT_DATA
;*/

Moved_down:

;/*
;** The screen Y origin of the overlap rectangle is the new rectangle's
;** Y coordinate, or the old rectangle's Y coordinate + |dy|.
;*/

        MOV     AL,BH
        ADD     [esi].rdOverlap.ptlScreen.ptl_y,EAX

;/*
;** Compute where the new top is.  This will be both the top of the new
;** rectangle and the overlap area.
;*/

        ADD     AL,DH                   ; DH = ptbSave.ptb_y
        AND     AL,SAVE_BUFFER_HEIGHT-1 ; CH = sizbMask.sizb_cy
        MOV     [EDI].POINTER_DATA.rd.ptbSave.ptb_y,AL
        MOV     [esi].rdOverlap.ptbSave.ptb_y,AL

;/*
;** The height of the read and flush buffers is |dy|.  The width is just the
;** working width.
;*/

        MOV     AH,BH
        MOV     AL,CL                   ; CL = sizbMask.sizb_cx
        MOV     [esi].rdFlushY.sizb,AX
        MOV     [esi].rdReadY.sizb,AX
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

;/*
;** The data to be flushed will come from the top of the old rectangle
;*/

        MOV     EAX,EDX
        MOV     [esi].rdFlushY.ptbSave,DX

;/*
;** The data to be read will go at the old top + sizbMask.sizb_cy
;*/

        ADD     AH,CH
        ADD     AL,BL
        AND     EAX,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        MOV     [esi].rdReadY.ptbSave,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1

;/*
;** The Y screen origin of the flush buffer is the old ptlScreen.ptl_y
;*/

        MOV     EAX,[ESI].SaveESI
        MOV     [esi].rdFlushY.ptlScreen.ptl_y,EAX

;/*
;** The height of the overlap rectangle is sizbMask.sizb_cy - |dy|.  This is
;** also the Y offset into the work area of the read rectangle.  The X offset
;** is zero.
;*/

        MOV     AH,CH
        SUB     AH,BH
        MOV     [esi].rdOverlap.sizb.sizb_cy,AH
        XOR     AL,AL
        MOV     [esi].rdReadY.ptbWork,AX
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1

;/*
;** The screen X origin of the read rectangle is the new rectangle's X
;** coordinate.  The Y coordinate can be computed as the old
;** rectangle's Y coordinate + the save height
;*/

        MOVZX   EAX,CH
        ADD     EAX,[ESI].SaveESI

Finish_y_overlap:
        MOV     [esi].rdReadY.ptlScreen.ptl_y,EAX
        MOV     EAX,[EDI].POINTER_DATA.rd.ptlScreen.ptl_x
        MOV     [esi].rdReadY.ptlScreen.ptl_x,EAX


;/*
;** The X address of the flush rectangle on the screen is ptlScreen.ptl_x.
;*/

        MOV     [esi].rdFlushY.ptlScreen.ptl_x,EBP
;/*
;** Set the flags to show that there is some form of Y overlap.  We want
;** to show that there is some Y rectangle to be read/flushed, and that
;** there is some overlap rectangle to be processed.
;*/

        OR      WORD ptr [ESI].fbFlush,((FB_READ_Y OR FB_OVERLAP) shl 8) + FB_FLUSH_Y
        .ERRNZ  CURSORSTRUCT.fbAndRead-CURSORSTRUCT.fbFlush-1

;/*
;** We have computed the separate X and Y componets of the overlap.  If there
;** was both dx and dy, then we have an L shaped area which we'll be reading/
;** writing.  In this case, we want to remove the overlapping portion of the L.
;*/

        OR      BL,BL
        JZ      Processed_y_overlap

;/*
;** We have something which looks like one of the following:
;**
;**   ----------               ----------
;**  |  flush   |             |  flush   |    dy > 0
;**  | f        |             |        f |
;**  | l  ----------       ----------  l | -----
;**  | u |      |   |     |   |      | u |   |
;**  | s |      |   |     |   |      | s | limit the "x" rectangles to
;**  | h |  1   | r |     | r |   2  | h | this height
;**  |   |      | e |     | e |      |   |   |
;**   ---|------  a |     | a  ------|---  -----
;**      |        d |     | d        |  \
;**      |   read   |     |   read   |\  \
;**       ----------       ----------  \  \
;**                                     \  \_____ The "x" overlap rectangle
;**                                      \
;**                                       \______ The "y" overlap rectangle
;**
;**
;**
;**   ----------               ----------     dy < 0
;**  |   read   |             |   read   |
;**  | r        |             |        r |
;**  | e  ------|---       ---|------  e | -----
;**  | a |      |   |     |   |      | a |   |
;**  | d |      | f |     | f |      | d | limit the "x" rectangles to
;**  |   |  3   | l |     | l |   4  |   | this height
;**  |   |      | u |     | u |      |   |   |
;**   ----------  s |     | s  ----------  -----
;**      |        h |     | h        |
;**      |  flush   |     |   flush  |
;**       ----------       ----------
;**
;**
;** The corners of the L shape are contained in both the X and Y rectangles we
;** just created.  We'll remove the overlap from the X rectangles.  To do
;** this, we must subtract |dy| from the height stored in the rectangles
;** (which is sizbMask.sizb_cy) and adjust X parameters of either the read or
;** flush rectangles.
;**
;** For cases 1 and 2, we want to adjust X parameters of the flush rectangle.
;** For cases 3 and 4, we want to adjust X parameters of the read rectangle.
;**
;** Currently:
;**        BH =        dy
;**        BL =        dx
;**        CH =        buffer height
;**        CL =        buffer width
;**        DH =        old pointer's Y coordinate in save area
;**        DL =        old pointer's X coordinate in save area
;**        SI =        old pointer's Y screen coordinate
;**        BP =        old pointer's X screen coordinate
;**        DI =        --> new pointer's RECT_DATA
;*/

        MOV     AL,BH                            ; EAX = dy
        LEA     EBX,[esi].rdFlushX               ; Assume cases 1 and 2
        OR      AL,AL
        JNS     @F
        LEA     EBX,[esi].rdReadX                ; It's cases 3 and 4
        NEG     AL                               ; EAX = |dy|
        ADD     [EBX].RECT_DATA.ptbWork.ptb_y,AL ; Move down in the work area,
                                                 ; too!
@@:
        MOV     CL,[EBX].RECT_DATA.ptbSave.ptb_y
        ADD     CL,AL
        AND     CL,SAVE_BUFFER_HEIGHT-1
        MOV     [EBX].RECT_DATA.ptbSave.ptb_y,CL
        MOVZX   EAX,AL
        .ERRNZ  (SAVE_BUFFER_HEIGHT-1) AND 80h
        ADD     [EBX].RECT_DATA.ptlScreen.ptl_y,EAX
        NEG     AL
        ADD     AL,CH   ; sizbMask.sizb_cy - |dy|
        MOV     [esi].rdFlushX.sizb.sizb_cy,AL
        MOV     [esi].rdReadX.sizb.sizb_cy,AL

Processed_y_overlap:
ComputeRects_exit:
ASSUME  ESI:NOTHING
        RET

ComputeRects ENDP

        SUBTITLE ClipRects
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ClipRects
;*
;* DESCRIPTION   = This routine clips the rectangles computed by ComputeRects.
;*
;*                 Registers Preserved:
;*                       BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = NONE
;*
;*
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
;/*
;** ECX passed in as pCurData
;** No need to save/restore ECX
;** Makes calls
;*/

ClipRects PROC SYSCALL PUBLIC uses EBX EDI EDX ESI
ASSUME  ECX:PCURSORSTRUCT

        LEA     ESI,[ECX].pdPtr0
        ADD     ESI,[ECX].ppdOld              ;ESI --> old POINTER_DATA structure
        MOV     DL,[ESI].POINTER_DATA.fb ; Already been clipped?
        TEST    DL,PD_CLIPPED                            ; (Won't be set if PD_VALID isn't set)
        JZ      Old_been_clipped        ;  Y - No need to do it again

;/*
;** The old pointer needs some form of clipping.  This can affect either the
;** old pointer's rectangle or rdFlushX and rdFlushY.  Since the old pointer's
;** rectangle will be discarded after it is restored, we don't care if we
;** write over its contents.
;*/

        MOV     EDI,ESI                 ; EDI --> rectangle to clip
        MOV     BL,[ECX].fbFlush              ; fbFlush tells us which rects to use
        MOV     BH,FB_OLD_PTR
        TEST    BL,BH
        JNZ     Call_do_clip                             ; Only have to clip old pointer's rect
        LEA     EDI,[ECX].rdFlushX
        MOV     BH,FB_FLUSH_X
        TEST    BL,BH
        JZ      @F

;/*
;** saves/restores ecx
;*/

        INVOKE  DoClipping
@@:
        LEA     EDI,[ECX].rdFlushY
        MOV     BH,FB_FLUSH_Y
        TEST    BL,BH
        JZ      @F

Call_do_clip:

;/*
;**saves/restores ecx
;*/

        INVOKE  DoClipping
@@:
        MOV     [ECX].fbFlush,BL
        LEA     EDI,[ECX].rdOverlap
        MOV     BL,[ECX].fbAndRead
        MOV     BH,FB_OVERLAP
        TEST    BL,BH
        JZ      @F
        ;saves/restores ecx
        INVOKE  DoClipping
        MOV     [ECX].fbAndRead,BL
@@:
Old_been_clipped:

;/*
;** The old pointer rectangle has been clipped.  Now see about clipping the
;** new pointer rectangle.
;*/

        LEA     ESI,[ECX].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[ecx].ppdNew

        MOV     DL,[ESI].POINTER_DATA.fb ; Already clipped?
        TEST    DL,PD_CLIPPED                            ; (Won't be set if PD_VALID isn't set)
        JZ      New_been_clipped                 ;  Y - Don't need to do it, then

;/*
;** The new rectangle structure needs to be clipped.  This presents a minor
;** problem in that we don't want to destroy the screen X,Y and buffer X,Y of
;** the pointer's POINTER_DATA structure.  What we'll do instead is to copy it
;** to the rdWork structure and update it there.  We'll also set up to XOR
;** this to the screen instead of the POINTER_DATA structure.
;*/

        LODSW
        MOV     [ECX].rdWork.ptbSave,AX
        .ERRNZ  RECT_DATA.ptbSave
        .ERRNZ  (sizeof POINTB) - 2

        LODSD
        MOV     [ECX].rdWork.ptlScreen.ptl_x,EAX
        .ERRNZ  RECT_DATA.ptlScreen-RECT_DATA.ptbSave-2
        .ERRNZ  (sizeof POINTL.ptl_x)-4
        .ERRNZ  POINTL.ptl_x

        LODSD
        MOV     [ECX].rdWork.ptlScreen.ptl_y,EAX
        .ERRNZ  POINTL.ptl_y-POINTL.ptl_x-4
        .ERRNZ  (sizeof POINTL.ptl_y)-4

        LODSW
        MOV     [ECX].rdWork.sizb,AX
        .ERRNZ  RECT_DATA.sizb-RECT_DATA.ptlScreen.ptl_y-4
        .ERRNZ  (sizeof SIZEB) - 2

        LODSW
        MOV     [ECX].rdWork.ptbWork,AX
        .ERRNZ  RECT_DATA.ptbWork-RECT_DATA.sizb-2
        .ERRNZ  (size POINTB) - 2

        SUB     ESI,sizeof RECT_DATA            ; ESI --> POINTER_DATA
        .ERRNZ  (RECT_DATA.ptbWork+2)-(size RECT_DATA)

;/*
;** Perform the clipping for the work area.  We know that some part of the
;** work area exists, else we wouldn't be here with a valid rectangle.
;** If FB_NEW_PTR is set in fbAndRead, then we want to replace it with
;** FB_WORK_RECT, else we'll want to process any overlap rectangles.
;*/

        LEA     EDI,[ECX].rdWork
        INVOKE  DoClipping
        MOV     BL,FB_WORK_RECT
        MOV     [ECX].fbXor,BL
        XCHG    [ECX].fbAndRead,BL                       ; Assume only work rect to and/read
        TEST    BL,FB_NEW_PTR
        JNZ     New_been_clipped
        LEA     EDI,[ECX].rdReadX
        MOV     BH,FB_READ_X
        TEST    BL,BH
        JZ      @F
        ;saves/restores ecx
        INVOKE  DoClipping
@@:
        LEA     EDI,[ECX].rdReadY
        MOV     BH,FB_READ_Y
        TEST    BL,BH
        JZ      @F
        ;saves/restores ecx
        INVOKE  DoClipping
@@:
        MOV     [ECX].fbAndRead,BL

New_been_clipped:

        RET

ClipRects ENDP

        SUBTITLE DoClipping
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = DoClipping
;*
;* DESCRIPTION   = This routine performs the actual clipping of a rectangle
;*                 using the passed POINTER_DATA and RECT_DATA structures.
;*
;*                 Registers Preserved:
;*                       DX,SI,DI,BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,BH,CX
;*
;* INPUT         = BL   =  flag byte
;*                 BH   =  bit to clear in BL if rectangle is invisible
;*                 DL   =  POINTER_DATA.fb for [ESI]
;*                 ESI --> POINTER_DATA structure
;*                 EDI --> RECT_DATA structure
;*
;* OUTPUT        = BL updated
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
;/*
;** Does Not Need or use CurData
;** Makes no calls
;** Must save/restore ECX
;*/

DoClipping PROC SYSCALL PUBLIC USES ECX
ASSUME  ECX:NOTHING

        .ERRNZ  POINTER_DATA.rd                         ; Must be at offset 0

        XOR     EAX,EAX
        TEST    DL,PD_CLIP_BOTTOM OR PD_CLIP_TOP
        JZ      Y_clipping_done
        MOV     ECX,[EDI].RECT_DATA.ptlScreen.ptl_y     ; ECX = y co-ord.
        JS      Clip_on_bottom_eh?
        .ERRNZ  PD_CLIP_BOTTOM-10000000b

;/*
;** Top clipping may have to be performed for this rectangle.
;*/

Clip_on_top:
        NEG     ECX                              ; If it was neg, must clip
        JLE     Y_clipping_done                  ;  N - No clipping needed
        SUB     [EDI].RECT_DATA.sizb.sizb_cy,CL  ;  Y - Compute new height
        JLE     Clear_visible_bit                ; Clipped away, nada visible
        ADD     [EDI].RECT_DATA.ptbWork.ptb_y,CL ; Move down in work area
        ADD     CL,[EDI].RECT_DATA.ptbSave.ptb_y ; Move down in save area
        AND     CL,SAVE_BUFFER_HEIGHT-1
        MOV     [EDI].RECT_DATA.ptbSave.ptb_y,CL
        MOV     [EDI].RECT_DATA.ptlScreen.ptl_y,EAX
        JMP     Finish_Y_clipping

;/*
;** Bottom clipping may have to be performed for this rectangle.
;*/

Clip_on_bottom_eh?:
        MOV     AL,[EDI].RECT_DATA.sizb.sizb_cy
        ADD     ECX,EAX
        SUB     ECX,SCREEN_CY
        JLE     Finish_Y_clipping                ; AX = amount to clip if pos.
        SUB     AL,CL                            ; Compute new height
        JLE     Clear_visible_bit                ; Clipped away, nada visible
        MOV     [EDI].RECT_DATA.sizb.sizb_cy,AL

Finish_Y_clipping:
        XOR     EAX,EAX

Y_clipping_done:
        TEST    DL,PD_CLIP_LEFT OR PD_CLIP_RIGHT
        JZ      X_clipping_done
        MOV     ECX,[EDI].RECT_DATA.ptlScreen.ptl_x
        TEST    DL,PD_CLIP_RIGHT
        JNZ     Clip_on_RHS

;/*
;** LHS clipping may have to be performed for this rectangle.
;*/

Clip_on_LHS:
        NEG     ECX                              ; If it was neg, must clip
        JLE     X_clipping_done                  ; Was pos, no clipping needed
        SUB     [EDI].RECT_DATA.sizb.sizb_cx,CL  ; Compute new width
        JLE     Clear_visible_bit                ; Clipped away, nada visible
        ADD     [EDI].RECT_DATA.ptbWork.ptb_x,CL ; Move right in work area
        ADD     CL,[EDI].RECT_DATA.ptbSave.ptb_x ; Move right in save area
        AND     CL,SAVE_BUFFER_WIDTH-1
        MOV     [EDI].RECT_DATA.ptbSave.ptb_x,CL
        MOV     [EDI].RECT_DATA.ptlScreen.ptl_x,EAX
        JMP     X_clipping_done

;/*
;** RHS clipping may have to be performed for this rectangle.
;*/

Clip_on_RHS:
        MOV     AL,[EDI].RECT_DATA.sizb.sizb_cx
        ADD     ECX,EAX
        SUB     ECX,SCREEN_CBSCAN
        JLE     X_clipping_done                  ; AX = amount to clip if positive
        SUB     AL,CL                            ; Compute new height
        JLE     Clear_visible_bit                ; Clipped away, nothing visible
        MOV     [EDI].RECT_DATA.sizb.sizb_cx,AL

X_clipping_done:
        XOR     BH,BH   ; 0 to cancel following XOR

Clear_visible_bit:
        XOR     BL,BH   ; Update visible bit
        RET

DoClipping ENDP

if SCAN_CNT EQ 768

;/***************************************************************************
;*
;* FUNCTION NAME = XORToScreen
;*
;* DESCRIPTION   = The Bank Set needs are determined, structures are set,
;*                 and the new lower-level xor routine(s) called.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = AL = fbXor
;*                 ES = EGAMem
;*                 DS = PtrData
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
;/*
;** ECX passed in as ptr to CurData
;** Does not need to save/restore ecx
;*/

XORToScreen PROC SYSCALL
ASSUME  ECX:PCURSORSTRUCT

        Call    GetEIPinEAX
Start1:
        ADD     EAX,_XORToScreen-Start1
        ;EAX now has absolute address of _XORToScreen
        mov     [ECX].pfnXorToScreen,eax

        LEA     ESI,[ECX].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[ecx].ppdNew
;/*
;** Does any part of the cursor wrap around the right edge
;** back to the left edge?
;*/

        movzx   eax, [esi].RECT_DATA.sizb.sizb_cx
                                                     ; eax = cxExt of cursor in bytes
        add     eax, [esi].RECT_DATA.ptlScreen.ptl_x ; ax = right edge of cursor in bytes
        cmp     eax, SCREEN_CBSCAN
        jle     @f
        Call    GetEIPinEAX
Start2:
        ADD     EAX,rhs_XORToScreen-Start2

;/*
;**EAX now has absolute address of rhs_XORToScreen
;*/
        mov     [ECX].pfnXorToScreen,eax      ; at least part of the cursor is in
                                              ; the last byte of the scan line.
@@:

;/*
;** Does any part of the cursor wrap around the left edge
;** to the right edge?
;*/

        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_x ; ax = right edge of cursor in bytes
        or      eax, eax                               ; if X is positive, then no wrap
        jns     @f

        Call    GetEIPinEAX
Start3:
        ADD     EAX,lhs_XORToScreen-Start3

;/*
;** EAX now has absolute address of lhs_XORToScreen
;*/

        mov     [ECX].pfnXorToScreen,eax      ; at least part of the cursor has
                                              ; left the left edge of screen.
@@:

;/*
;** Is the top of the cursor off the top of the screen?
;*/

        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_y ; ax = top of cursor
        or      eax, eax
        jns     @f
        jmp     xts_y_negative
@@:

;/*
;** Find out if the cursor spans two banks.
;*/
        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_y ; ax = top of cursor
        movzx   edx, [esi].RECT_DATA.sizb.sizb_cy
        add     edx, eax                              ; dx = bottom of the cursor
        and     eax, 0200h                            ; ax = 0 < 512, ax != 0 > 512
        and     edx, 0200h                            ; dx = 0 < 512, ax != 0 > 512
        xor     eax, edx
        jnz     @f
        jmp     xts_mono_bank
@@:
;/*
;** We only get here if the cursor spans a bank
;** Make a bank 0 and a bank 1 copy of the rectangle structure
;*/

        xchg    ecx,eax                         ;SAVE ECX
ASSUME  EAX:PCURSORSTRUCT
ASSUME  ECX:NOTHING

        lea     edi,[EAX].rdBank0
        mov     ecx, (SIZE RECT_DATA) / 2
        cld
        rep     movsw

        .ERRNZ  SIZEOF RECT_DATA AND 1

        LEA     ESI,[EAX].pdPtr0
        ADD     ESI,[EAX].ppdNew
        lea     edi,[EAX].rdBank1
        mov     ecx, (SIZE RECT_DATA) / 2
        rep     movsw
        .ERRNZ  SIZEOF RECT_DATA AND 1

        lea     esi,[EAX].rdWork
        lea     edi,[EAX].rdWorkBank0
        mov     ecx, (SIZE RECT_DATA) / 2
        rep     movsw
        .ERRNZ  SIZEOF RECT_DATA AND 1

        lea     esi,[EAX].rdWork
        lea     edi,[EAX].rdWorkBank1
        mov     ecx, (SIZE RECT_DATA) / 2
        rep     movsw
        .ERRNZ  SIZEOF RECT_DATA AND 1

;/*
;** Adjust the rect data dimensions for bank 0 and bank 1
;*/

        lea     esi,[EAX].rdBank0
        lea     edi,[EAX].rdBank1

        mov     ecx,SCREEN_LPB
        sub     ecx,[esi].RECT_DATA.ptlScreen.ptl_y ; cx = bank 0 cyExt
        mov     [esi].RECT_DATA.sizb.sizb_cy,cl    ; bank 0 cyExt
        mov     [EAX].rdWorkBank0.sizb.sizb_cy,cl

        sub     [edi].RECT_DATA.sizb.sizb_cy,cl    ; bank 1 cyExt
        sub     [EAX].rdWorkBank1.sizb.sizb_cy,cl

        add     [edi].RECT_DATA.ptbSave.ptb_y,cl   ; save y
        and     [edi].RECT_DATA.ptbSave.ptb_y,SAVE_BUFFER_HEIGHT - 1
        add     [edi].RECT_DATA.ptlScreen.ptl_y,ecx ; screen y
        add     [edi].RECT_DATA.ptbWork.ptb_y,cl   ; work y

        add     [EAX].rdWorkBank1.ptbSave.ptb_y,cl   ; save y
        and     [EAX].rdWorkBank1.ptbSave.ptb_y,SAVE_BUFFER_HEIGHT - 1
        add     [EAX].rdWorkBank1.ptlScreen.ptl_y,ecx ; screen y
        add     [EAX].rdWorkBank1.ptbWork.ptb_y,cl   ; work y


        xchg    ecx,eax                         ;Restore ECX
ASSUME  ECX:PCURSORSTRUCT
ASSUME  EAX:NOTHING

;/*
;** Do Bank 0
;*/

        test    [ECX].fbXor,FB_WORK_RECT
        jz      @f
        lea     esi,[ECX].rdWorkBank0
@@:
        cmp     [esi].RECT_DATA.sizb.sizb_cy,0
        je      @f

        mov     edx,1
        SetBankR

        mov     edx,0
        SetBankW

        mov     al,[ECX].fbXor
        call    [ECX].pfnXorToScreen
@@:

;/*
;** Do Bank 1
;*/

        lea     esi,[ECX].rdBank1
        test    [ECX].fbXor,FB_WORK_RECT
        jz      @f
        LEA     esi,[ECX].rdWorkBank1
@@:
        cmp     [esi].RECT_DATA.sizb.sizb_cy, 0
        je      @f

        mov     edx, 1
        SetBankW

        mov     al,[ECX].fbXor
        or      al,FB_WORK_RECT
        call    [ECX].pfnXorToScreen
@@:
        jmp     xts_exit

xts_y_negative:

;/*
;** Y is negative, off the top
;** Clipping is needed.
;*/

        xchg    ecx,edx                         ;SAVE ECX
ASSUME  EDX:PCURSORSTRUCT
ASSUME  ECX:NOTHING

        lea     edi,[EDX].rdBank0
        mov     ecx, (SIZE RECT_DATA) / 2
        cld
        rep     movsw

        lea     esi,[EDX].rdBank0

        neg     eax                                   ; ax = top overhang
        sub     [esi].RECT_DATA.sizb.sizb_cy,al    ; adjust cyExt
        add     [esi].RECT_DATA.ptbSave.ptb_y,al   ; adjust save area y
        add     [esi].RECT_DATA.ptbWork.ptb_y,al   ; adjust work area y
        mov     [esi].RECT_DATA.ptlScreen.ptl_y,0  ; adjust screen y

        xchg    ecx,edx                         ;Restore ECX
ASSUME  ECX:PCURSORSTRUCT
ASSUME  EDX:NOTHING

        mov     edx, 1
        SetBankR

        mov     edx, 0
        SetBankW

        mov     al,[ECX].fbXor
        call    [ECX].pfnXorToScreen

        jmp     xts_exit


xts_mono_bank:

;/*
;** Set the read bank to the bank containing the work area.
;*/

        mov     edx, 1
        SetBankR

;/*
;** Set the screen bank
;*/


;/*
;** Assume new pointer is in use
;*/

        LEA     ESI,[ECX].pdPtr0
        ADD     ESI,[ECX].ppdNew

        test    [ECX].fbXor, FB_WORK_RECT
        jz      @f
        LEA     esi,[ECX].rdWork
@@:

        mov     eax,[esi].RECT_DATA.ptlScreen.ptl_y
        mov     edx,SCREEN_CBSCAN
        mul     dx                                ; dx = bank, ax = offset in bank
        SetBankW                                  ; set the write bank

        mov     al,[ECX].fbXor
        call    [ECX].pfnXorToScreen
xts_exit:
        ret
XORToScreen ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = lhs_XORToScreen
;*
;* DESCRIPTION   = The work area is XORed with the XOR mask and placed on the
;*                 screen Pointer is hanging over left hand side of screen.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = al = fbXor
;*                 si = -> rect_data
;*                 ES = EGAMem
;*                 DS = PtrData
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** ECX passed in as ptr to CurData
;** Does need to save/restore ecx
;*/

lhs_XORToScreen PROC SYSCALL
ASSUME  EDI:PCURSORSTRUCT
ASSUME  ECX:NOTHING
        MOV     EDI,ECX
        mov     ebx,[ESI].RECT_DATA.ptlScreen.ptl_x     ;x = 0 to -5
        neg     ebx                                     ;    0 to  5
        mov     dl,[EDI].abLhsOverhang[EBX]
        mov     ch,[esi].RECT_DATA.sizb.sizb_cx         ;ch is sizb_cx
        mov     cl,5
        sub     cl,ch                                   ;cl=5-sizb_cx
        shr     dl,cl
        mov     [EDI].bRLhsOverhang,dl
        jmp     rest_of_xor
lhs_XORToScreen ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = rhs_XORToScreen
;*
;* DESCRIPTION   = The work area is XORed with the XOR mask and placed on
;*                 the screen Pointer is hanging over right hand side of
;*                 screen.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = al = fbXor
;*                 si = -> rect_data
;*                 ES = EGAMem
;*                 DS = PtrData
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

rhs_XORToScreen PROC SYSCALL
ASSUME  EDI:PCURSORSTRUCT
ASSUME  ECX:NOTHING
        PUSH    ECX

        MOV     EDI,ECX

        movzx   ecx,[esi].RECT_DATA.sizb
;/*
;** Calculate the Right Hand Side Overhang.
;** Then save the bit vector
;*/

        movzx   ebx,cl                              ; bx = pointer width
        add     ebx,[esi].RECT_DATA.ptlScreen.ptl_x ; bx = rhs of pointer on screen
        sub     ebx,SCREEN_CBSCAN                   ; bx = byte of overhang
        mov     dl,[edi].abRhsOverhang[EBX]         ; dl = overhang bit vector
        mov     [EDI].bRLhsOverhang,dl              ; save overhang bit vector
rest_of_xor::

        clc
        .errnz  POINTER_DATA.rd                   ;Must be at offset 0
        xor     ebx,ebx                           ;Zero will be useful soon
        shr     al,1                              ;Set 'C' to FB_WORK_RECT bit
        .errnz  FB_WORK_RECT-00000001b

;/*
;** Program the EGA for XOR mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_XOR, and setting GRAF_BIT_MASK to FF.  The normal
;** sequence of events will have left the bitmask register with 00h and
;** the mode register in AND mode.
;*/

        mov     edx,EGA_BASE + GRAF_ADDR
        mov     eax,0FF00h + GRAF_BIT_MASK
        out     dx,ax                             ;Enable all bits
        mov     eax,DR_XOR shl 8 + GRAF_DATA_ROT
        out     dx,ax

;/*
;** Compute the offset from the start of the work area and the XOR mask
;** (its the same value).  If we're to use the new pointer's rectangle,
;** then this offset is zero.
;*/

        jnc     have_rhs_xor_mask_offset    ;BX = 0 is offset
        .errnz  FB_WORK_RECT-00000001b
        .erre   FB_NEW_PTR-00000001b

        xor     ebx,ebx
        movzx   eax,[esi].RECT_DATA.ptbWork      ;Get origin in work area
        xchg    ah,bl                   ;BX = ptbWork.ptb_y, AX = ptbWork.ptb_x
        add     eax,ebx                           ;*1 + X component
        add     ebx,ebx                           ;*2
        add     ebx,ebx                           ;*4
        add     ebx,eax                           ;*5 + X = start from work/mask
        .errnz  WORK_WIDTH-5
have_rhs_xor_mask_offset:


;/*
;** Compute the screen address of this rectangle and get it's size
;*/

        MOV     ECX,[EDI].selScreenPtr
        PUSH    EDI     ;EDI is PCURSORSTRUCT

        imul    edi,[esi].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        CMP     EDI,010000h
        jl      @F
        SUB     EDI,010000h
@@:
        add     edi,[esi].RECT_DATA.ptlScreen.ptl_x
        add     edi,ecx         ;+=selScreenPtr
        XCHG    EDI,DWORD PTR [ESP]     ;VRAM pointer on stack
                                        ;PCURSORSTRUCT in EDI
        movzx   ecx,[esi].RECT_DATA.sizb
        .errnz  SIZEB.sizb_cy-SIZEB.sizb_cx-1

;/*
;** To save incrementing the work area pointer (BX), subtract the XOR
;** mask pointer off of it.  Then use [BX][SI] for addressing into the
;** XOR mask.  As SI is incremented, BX will effectively be incremented.
;** We could not do this if the XOR mask and the work area were different
;** widths.
;**
;** Finish computing the pointer to the XOR mask and the delta from the
;** XOR mask to the work area.
;*/

        mov     esi,[EDI].pAndXor                ;Set address of XOR mask
        lea     esi,[esi][ebx][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]

        ADD     EBX,[EDI].selScreenPtr
        add     ebx,abPointerWork
        sub     ebx,esi

;/*
;** Compute the delta to the start of the next scanline of the XOR mask
;** and the work area, and the next scan of the screen
;*/

        movzx   eax,cl          ;EAX is sizb_cx
        mov     ebp,WORK_WIDTH
        sub     ebp,eax

        mov     edx, 12
        mul     edx             ;EDX:EAX=sizeb_cx * 12
        lea     edx,[ebp][SCREEN_CBSCAN-WORK_WIDTH]

        neg     eax
        PUSH    EBX
        Call    GetEIPinEBX
Start1:
        ADD     EBX,rhs_XORToScreen_width_0-Start1

;/*
;** EBX now has absolute address of rhs_XORToScreen_width_0
;*/

        ADD     EBX,EAX

        mov     ah,[EDI].bRLhsOverhang           ; set the overhang mask

        MOV     EDI,EBX
        POP     EBX
        XCHG    EDI,DWORD PTR [ESP]

        jmp     DWORD PTR [ESP]                                          ; enter the loop

        .errnz  WORK_WIDTH-5
        .errnz  rhs_XORToScreen_width_0-rhs_XORToScreen_width_1-12
        .errnz  rhs_XORToScreen_width_1-rhs_XORToScreen_width_2-12
        .errnz  rhs_XORToScreen_width_2-rhs_XORToScreen_width_3-12
        .errnz  rhs_XORToScreen_width_3-rhs_XORToScreen_width_4-12
        .errnz  rhs_XORToScreen_width_4-rhs_XORToScreen_width_5-12

;/*
;** Register usage for the loop will be:
;**
;**       AH    = Right/Left hand side AND bit vector
;**       AL    =
;**       BX    = offset off [si] to the work area
;**       CL    =
;**       CH    = height
;**       DX    = delta to next scan of the destination
;**       DS:SI --> XOR mask
;**       ES:DI --> Destination
;**       BP    = offset to next byte of XOR mask, next scan of work area
;*/

rhs_XORToScreen_width_5:
        cmp     al,[ebx][esi]                     ; Load latches from work area
        lodsb                                     ; Load al from XOR mask
        test    ah,10000b                         ; do we write this byte?
        jz      @f
        mov     [edi],al                          ; dest = mask XOR source
@@:
        inc     edi                               ; dest = dest
rhs_XORToScreen_width_4:
        cmp     al,[ebx][esi]                     ; Load latches from work area
        lodsb                                     ; Load al from XOR mask
        test    ah, 01000b                        ; do we write this byte?
        jz      @f
        mov     [edi], al                         ; dest = mask XOR source
@@:
        inc     edi                               ; dest = dest
rhs_XORToScreen_width_3:
        cmp     al,[ebx][esi]                     ; Load latches from work area
        lodsb                                     ; Load al from XOR mask
        test    ah, 00100b                        ; do we write this byte?
        jz      @f
        mov     [edi], al                         ; dest = mask XOR source
@@:
        inc     edi                               ; dest = dest
rhs_XORToScreen_width_2:
        cmp     al,[ebx][esi]                     ; Load latches from work area
        lodsb                                     ; Load al from XOR mask
        test    ah, 00010b                        ; do we write this byte?
        jz      @f
        mov     [edi], al                         ; dest = mask XOR source
@@:
        inc     edi                               ; dest = dest
rhs_XORToScreen_width_1:
        cmp     al,[ebx][esi]                     ; Load latches from work area
        lodsb                                     ; Load al from XOR mask
        test    ah, 00001b                        ; do we write this byte?
        jz      @f
        mov     [edi], al                         ; dest = mask XOR source
@@:
        inc     edi                               ; dest = dest
rhs_XORToScreen_width_0:
        add     esi,ebp                           ;--> next scan's XOR mask, work addr
        add     edi,edx                           ;--> next scan's destination
        dec     ch
        jz      @F
        jmp     DWORD PTR [ESP]
@@:
        ADD     ESP,4   ;get rid of the jmp address
        POP     ECX
        ret

rhs_XORToScreen ENDP

        SUBTITLE XORToScreen
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME =  _XORToScreen
;*
;* DESCRIPTION   = The work area is XORed with the XOR mask and placed on the
;*                 screen.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = AL = fbXor
;*                 DS = PtrData
;*
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

_XORToScreen PROC SYSCALL PUBLIC

;/*
;** ECX passed in as ptr to CurData
;** Does need to save/restore ecx
;** Makes no calls
;*/
        PUSH    ECX
ASSUME  ECX:PCURSORSTRUCT
        .ERRNZ  POINTER_DATA.rd         ; Must be at offset 0

        XOR     EBX,EBX                 ; Zero will be useful soon
        SHR     AL,1                    ; Set 'C' to FB_WORK_RECT bit
        .ERRNZ  FB_WORK_RECT-00000001b

;/*
;** Program the EGA for XOR mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_XOR, and setting GRAF_BIT_MASK to FF.  The normal sequence
;** of events will have left the bitmask register with 00h and the mode
;** register in AND mode.
;*/

        MOV     EDX,EGA_BASE + GRAF_ADDR
        MOV     EAX,0FF00h + GRAF_BIT_MASK
        OUT     DX,AX                           ; Enable all bits
        MOV     EAX,DR_XOR SHL 8 + GRAF_DATA_ROT
        OUT     DX,AX

;/*
;** Compute the offset from the start of the work area and the XOR mask (it's
;** the same value).  If we're to use the new pointer's rectangle, then this
;** offset is zero.
;*/


        JNC     Have_XOR_mask_offset            ; EBX = 0 is offset
        .ERRNZ  FB_WORK_RECT-00000001b
        .ERRE   FB_NEW_PTR-00000001b
        MOVZX   EAX,[ESI].RECT_DATA.ptbWork     ; Get origin in work area
        XCHG    AH,BL                           ; BX = ptbWork.ptb_y,
                                                ; AX = ptbWork.ptb_x
        ADD     EAX,EBX                         ; *1 + X component
        SHL     EBX,2                           ; *4
        ADD     EBX,EAX                         ; *5 + X = start from
                                                ;          work/mask
        .ERRNZ  WORK_WIDTH-5

Have_XOR_mask_offset:


;/*
;** Compute the screen address of this rectangle and get it's size.
;*/

        IMUL    EDI,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        CMP     EDI,010000h
        jl      @F
        SUB     EDI,010000h
@@:
        ADD     EDI,[ESI].RECT_DATA.ptlScreen.ptl_x
        ADD     EDI,[ECX].selScreenPtr
        MOVZX   EAX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

;/*
;** To save incrementing the work area pointer (EBX), subtract the XOR mask
;** pointer off of it.  Then use [EBX][ESI] for addressing into the XOR mask.
;** As ESI is incremented, EBX will effectively be incremented.  We could not
;** do this if the XOR mask and the work area were different widths.
;**
;** Finish computing the pointer to the XOR mask and the delta from the XOR
;** mask to the work area.
;*/

        MOV     ESI,[ECX].pAndXor
        LEA     ESI,[ESI][EBX][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks] ; ESI --> XOR mask
        ADD     EBX,[ECX].selScreenPtr
        ADD     EBX,abPointerWork               ; EBX --> start in work area
                                                ;         (which is in video
                                                ;          memory)
        SUB     EBX,ESI                         ; EBX --> start in work area
                                                ;         relative to XOR mask
;/*
;** Compute the delta to the start of the next scanline of the XOR mask and the
;** work area, and the next scan of the screen
;*/

        MOV     ECX,EAX
ASSUME ECX:NOTHING
        MOVZX   EAX,AL
        MOV     EBP,WORK_WIDTH
        SUB     EBP,EAX
        LEA     EDX,[EBP][SCREEN_CBSCAN-WORK_WIDTH]
        SHL     EAX,2
        NEG     EAX
        PUSH    EBX
        Call    GetEIPinEBX
Start1:
        ADD     EBX,_XORToScreen_width_0-Start1

;/*
;** EBX now has absolute address of _XORToScreen_width_0
;*/

        ADD     EAX,EBX
        POP     EBX
        JMP     EAX

        .ERRNZ  _XORToScreen_width_0-_XORToScreen_width_1-4
        .ERRNZ  _XORToScreen_width_1-_XORToScreen_width_2-4
        .ERRNZ  _XORToScreen_width_2-_XORToScreen_width_3-4
        .ERRNZ  _XORToScreen_width_3-_XORToScreen_width_4-4
        .ERRNZ  _XORToScreen_width_4-_XORToScreen_width_5-4
        .ERRNZ  WORK_WIDTH-5

;/*
;** Register usage for the loop will be:
;**
;**        EAX  =  Loop starting address
;**        EBX  =  Offset off [ESI] to the work area
;**        CL   =
;**        CH   =  Height
;**        EDX  =  Delta to next scan of the destination
;**        ESI --> XOR mask
;**        EDI --> Destination
;**        EBP  =  Offset to next byte of XOR mask, next scan of work area
;*/

_XORToScreen_width_5:
        CMP     AL,[EBX][ESI]                    ; Load latches from work area
        MOVSB                                    ; XOR to the screen
_XORToScreen_width_4:
        CMP     AL,[EBX][ESI]
        MOVSB
_XORToScreen_width_3:
        CMP     AL,[EBX][ESI]
        MOVSB
_XORToScreen_width_2:
        CMP     AL,[EBX][ESI]
        MOVSB
_XORToScreen_width_1:
        CMP     AL,[EBX][ESI]
        MOVSB
_XORToScreen_width_0:
        ADD     ESI,EBP         ; ESI --> next scan's XOR mask, work addr
        ADD     EDI,EDX         ; EDI --> next scan's destination
        DEC     CH
        JZ      @F
        JMP     EAX             ; Next pass
@@:
        POP     ECX
        RET

_XORToScreen ENDP
else ;768 - SEE POINTER3.asm for the rest of the 768 stuff...
        SUBTITLE XORToScreen
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = XORToScreen
;*
;* DESCRIPTION   = The work area is XORed with the XOR mask and placed on the
;*                 screen.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = AL = fbXor
;*                 DS = PtrData
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

XORToScreen PROC SYSCALL PUBLIC
;/*
;** ECX passed in as ptr to CurData
;** Does not need to save/restore ecx
;** Makes no calls
;*/

ASSUME  ECX:PCURSORSTRUCT
        .ERRNZ  POINTER_DATA.rd         ; Must be at offset 0

        XOR     EBX,EBX                 ; Zero will be useful soon

        LEA     ESI,[ECX].pdPtr0        ; ESI --> new POINTER_DATA structure
        ADD     ESI,[ecx].ppdNew

        SHR     AL,1                    ; Set 'C' to FB_WORK_RECT bit
        .ERRNZ  FB_WORK_RECT-00000001b

;/*
;** Program the EGA for XOR mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_XOR, and setting GRAF_BIT_MASK to FF.  The normal sequence
;** of events will have left the bitmask register with 00h and the mode
;** register in AND mode.
;*/

        MOV     EDX,EGA_BASE + GRAF_ADDR
        MOV     EAX,0FF00h + GRAF_BIT_MASK
        OUT     DX,AX                           ; Enable all bits
        MOV     EAX,DR_XOR SHL 8 + GRAF_DATA_ROT
        OUT     DX,AX

;/*
;** Compute the offset from the start of the work area and the XOR mask (it's
;** the same value).  If we're to use the new pointer's rectangle, then this
;** offset is zero.
;*/


        JNC     Have_XOR_mask_offset            ; EBX = 0 is offset
        .ERRNZ  FB_WORK_RECT-00000001b
        .ERRE   FB_NEW_PTR-00000001b
        LEA     ESI,[ECX].rdWork                ; Work area we're clipping
        MOVZX   EAX,[ESI].RECT_DATA.ptbWork     ; Get origin in work area
        XCHG    AH,BL                           ; BX = ptbWork.ptb_y,
                                                ; AX = ptbWork.ptb_x
        ADD     EAX,EBX                         ; *1 + X component
        SHL     EBX,2                           ; *4
        ADD     EBX,EAX                         ; *5 + X = start from
                                                ;          work/mask
        .ERRNZ  WORK_WIDTH-5

Have_XOR_mask_offset:


;/*
;** Compute the screen address of this rectangle and get it's size.
;*/

        IMUL    EDI,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        ADD     EDI,[ESI].RECT_DATA.ptlScreen.ptl_x
        ADD     EDI,[ECX].selScreenPtr
        MOVZX   EAX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

;/*
;** To save incrementing the work area pointer (EBX), subtract the XOR mask
;** pointer off of it.  Then use [EBX][ESI] for addressing into the XOR mask.
;** As ESI is incremented, EBX will effectively be incremented.  We could not
;** do this if the XOR mask and the work area were different widths.
;**
;** Finish computing the pointer to the XOR mask and the delta from the XOR
;** mask to the work area.
;*/

        MOV     ESI,[ECX].pAndXor
        LEA     ESI,[ESI][EBX][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks] ; ESI --> XOR mask
        ADD     EBX,[ECX].selScreenPtr
        ADD     EBX,abPointerWork               ; EBX --> start in work area
                                                ;         (which is in video
                                                ;          memory)
        SUB     EBX,ESI                         ; EBX --> start in work area
                                                ;         relative to XOR mask
;/*
;** Compute the delta to the start of the next scanline of the XOR mask and the
;** work area, and the next scan of the screen
;*/

        MOV     ECX,EAX
ASSUME ECX:NOTHING
        MOVZX   EAX,AL
        MOV     EBP,WORK_WIDTH
        SUB     EBP,EAX
        LEA     EDX,[EBP][SCREEN_CBSCAN-WORK_WIDTH]
        SHL     EAX,2
        NEG     EAX
        PUSH    EBX
        Call    GetEIPinEBX
Start1:
        ADD     EBX,XORToScreen_width_0-Start1

;/*
;** EBX now has absolute address of XORToScreen_width_0
;*/

        ADD     EAX,EBX
        POP     EBX
        JMP     EAX

        .ERRNZ  XORToScreen_width_0-XORToScreen_width_1-4
        .ERRNZ  XORToScreen_width_1-XORToScreen_width_2-4
        .ERRNZ  XORToScreen_width_2-XORToScreen_width_3-4
        .ERRNZ  XORToScreen_width_3-XORToScreen_width_4-4
        .ERRNZ  XORToScreen_width_4-XORToScreen_width_5-4
        .ERRNZ  WORK_WIDTH-5

;/*
;** Register usage for the loop will be:
;**
;**        EAX  =  Loop starting address
;**        EBX  =  Offset off [ESI] to the work area
;**        CL   =
;**        CH   =  Height
;**        EDX  =  Delta to next scan of the destination
;**        ESI --> XOR mask
;**        EDI --> Destination
;**        EBP  =  Offset to next byte of XOR mask, next scan of work area
;*/

XORToScreen_width_5:
        CMP     AL,[EBX][ESI]                    ; Load latches from work area
        MOVSB                                    ; XOR to the screen
XORToScreen_width_4:
        CMP     AL,[EBX][ESI]
        MOVSB
XORToScreen_width_3:
        CMP     AL,[EBX][ESI]
        MOVSB
XORToScreen_width_2:
        CMP     AL,[EBX][ESI]
        MOVSB
XORToScreen_width_1:
        CMP     AL,[EBX][ESI]
        MOVSB
XORToScreen_width_0:
        ADD     ESI,EBP         ; ESI --> next scan's XOR mask, work addr
        ADD     EDI,EDX         ; EDI --> next scan's destination
        DEC     CH
        JZ      @F
        JMP     EAX             ; Next pass
@@:
        RET

XORToScreen ENDP

        SUBTITLE ColorPointerToScreen
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ColorPointerToScreen
;*
;* DESCRIPTION   = The color pointer is output to the screen using
;*                 AND/XOR/COLOR masks.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** ECX is passed in as pCurData
;** ECX is saved/restored
;** This calls CPSDoAPass passing in pCurData as SavePtr
;*/

ColorPointerToScreen PROC SYSCALL PUBLIC USES ECX
        LOCAL   chPlaneMask :BYTE
        LOCAL   ulDelScreen :DWORD
        LOCAL   ulOffsetSave:DWORD
        LOCAL   ulOffsetMask:DWORD
        LOCAL   SavePtr:DWORD
        LOCAL   Save1:DWORD

ASSUME  ECX:PCURSORSTRUCT
        MOV     SavePtr,ECX

        .ERRNZ  POINTER_DATA.rd         ; Must be at offset 0
        MOV     chPlaneMask,MM_C3        ; Start from plane C3
        .ERRNZ  MM_C3-00001000b

;/*
;** Program the EGA for SET mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_SET, and setting GRAF_BIT_MASK to FF.  The normal sequence
;** of events will have left the bitmask register with 00h and the mode
;** register in AND mode.
;*/

        MOV     EDX,EGA_BASE + GRAF_ADDR
        MOV     EAX,0FF00h + GRAF_BIT_MASK
        OUT     DX,AX                           ; Enable all bits
        MOV     EAX,DR_SET SHL 8 + GRAF_DATA_ROT
        OUT     DX,AX
CPS_do_next_plane:

;/*
;** Set write plane enable register to the ONE plane we will alter.
;*/

        MOV     AL,chPlaneMask           ; Set Map Mask
        MOV     EDX,EGA_BASE + SEQ_DATA
        OUT     DX,AL

;/*
;** Set read plane to plane to read from.
;*/

        SHR     AL,1                    ; Map plane into ReadMask
        CMP     AL,100b                 ; Set Carry if not C3 (plane 3)
        ADC     AL,-1                   ; Sub 1 only if C3
        MOV     AH,AL
        MOV     AL,GRAF_READ_MAP
        MOV     DL,GRAF_ADDR
        OUT     DX,AX

        LEA     ESI,[ECX].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[ecx].ppdNew

        TEST    [ECX].fbXor,FB_WORK_RECT
        JZ      CPS_have_pointer_rect
        LEA     ESI,[ECX].rdWork               ; ESI --> Work area definition

CPS_have_pointer_rect:

;/*
;** Compute the screen address of this rectangle and get it's size
;*/

        IMUL    EDI,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        ADD     EDI,[ESI].RECT_DATA.ptlScreen.ptl_x     ; EDI --> Offset w/i
                                                        ;         screen
        ADD     EDI,[ECX].selScreenPtr
        MOVZX   EAX,[ESI].RECT_DATA.ptbSave
        MOVZX   EDX,[ESI].RECT_DATA.ptbWork
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
        MOVZX   ECX,[ESI].RECT_DATA.sizb
ASSUME  ECX:NOTHING
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

        XOR     EBX,EBX
        MOV     BL,AL                   ; See if save area is contiguous
        ADD     BL,CL
        SUB     BL,SAVE_BUFFER_WIDTH    ; BL = is overhang
        JLE     Call_CPSDoAPass         ; No overhang

        SUB     CL,BL                   ; Set X extent for pass 1
        MOV     BH,CL                   ; Need it for pass 2
        PUSH    ESI                     ; Must keep rectangle pointer around
        PUSH    EDI
        PUSH    EBX
        INVOKE  CPSDoAPass              ; Process first half
        POP     EBX
        POP     EDI
        POP     ESI
        MOVZX   EAX,[ESI].RECT_DATA.ptbSave
        MOVZX   EDX,[ESI].RECT_DATA.ptbWork
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
        MOVZX   ECX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

        ADD     DL,BH                   ; Move right in work area
        MOV     CL,BL                   ; Set new extent
        XOR     AL,AL                   ; X origin in save area is 0
        MOV     BL,BH                   ; Move right in destination area
        XOR     BH,BH
        ADD     EDI,EBX

Call_CPSDoAPass:
        INVOKE  CPSDoAPass              ; Process second half

        MOV     ECX,SavePtr
ASSUME  ECX:PCURSORSTRUCT
        SHR     chPlaneMask,1
        JNC     CPS_do_next_plane
        .ERRNZ  MM_C0-00000001b
        .ERRNZ  MM_C1-00000010b
        .ERRNZ  MM_C2-00000100b
        .ERRNZ  MM_C3-00001000b

        RET

ColorPointerToScreen ENDP

        SUBTITLE CPSDoAPass
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = CPSDoAPass
;*
;* DESCRIPTION   = Output one plane of the color pointer to the screen using
;*                 AND/XOR/COLOR masks.
;*
;*                 Registers Preserved:
;*                       DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,FLAGS
;*
;* INPUT         = EBP  =  Stack frame of ColorPointerToScreen
;*                 AH   =  ptbSave.ptb_y
;*                 AL   =  ptbSave.ptb_x
;*                 DH   =  ptbWork.ptb_y
;*                 DL   =  ptbWork.ptb_x
;*                 CL   =  X bytes to process
;*                 CH   =  Y scans to process
;*                 EDI --> Destination (screen area)
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

;/*
;** This makes no calls
;** Gets pCurData passed in as SavePtr
;** Does not need to save/restore anything
;*/

CPSDoAPass PROC SYSCALL PUBLIC

        LOCAL   chPlaneMask :BYTE
        LOCAL   ulDelScreen :DWORD
        LOCAL   ulOffsetSave:DWORD
        LOCAL   ulOffsetMask:DWORD
        LOCAL   SavePtr:DWORD
        LOCAL   Save1:DWORD

;/*
;** NOTE:  Even  though  the above local variables are defined, no  frame  is
;** created.   The  locals are defined simply to be able to access the  local
;** variables defined in the calling function, ColorPointerToScreen.  This is
;** done  primarily  to  save the execution time needed to create a new frame
;** each time this function is called.
;*/

;/*
;** Compute the offset within the save area.  Note that this is relative to
;** screen_buf.
;** AH = ptbSave.ptb_y, AL = ptbSave.ptb_x
;*/

        SHL     AH,3
        ADD     AL,AH                   ; y * 8 + x
        MOVZX   EAX,AL
        MOV     ulOffsetSave,EAX
        .ERRNZ  SAVE_BUFFER_WIDTH-8
        .ERRNZ  SAVE_BUFFER_HEIGHT-32

        PUSH    EDI

        MOV     EDI,SavePtr
ASSUME  EDI:PCURSORSTRUCT
        MOV     ESI,[EDI].selScreenPtr
        ADD     ESI,abPointerSave
        MOV     Save1,ESI
        ADD     ESI,EAX                 ; ESI --> start address of save area

;/*
;** Compute the offset within the AND/XOR masks.
;** DH = ptbWork.ptb_y, DL = ptbWork.ptb_x
;*/

        XOR     EBX,EBX
        XCHG    DH,BL                   ; DX = x, BX = y
        ADD     EDX,EBX                 ; DX = y + x
        SHL     EBX,2                   ; EBX = y * 4
        ADD     EBX,EDX                 ; EBX = y * 5 + x

        MOV     EAX,EBX
        ADD     EAX,[EDI].pColor
        MOV     ulOffsetMask,EAX
        ADD     EBX,[EDI].pAndXor       ; EBX --> start address of AND mask

        POP     EDI
ASSUME  EDI:NOTHING

        SUB     EBX,EDI                 ; Make EBX relative to EDI

;/*
;** Compute the delta to the start of the next scanline of destination screen
;** area.
;*/

        MOVZX   EAX,CL
        MOV     EDX,SCREEN_CBSCAN
        SUB     EDX,EAX
        MOV     ulDelScreen,EDX         ; Delta to next scan of screen area

;/*
;** Calculate EDX = jump table address.
;*/

        IMUL    EAX,15          ; 15x
        Call    GetEIPinEDX
Start2:
        ADD     EDX,Color_to_screen_width_0-Start2
        ;EDX now has absolute address of Color_to_screen_width_0
        SUB     EDX,EAX
        .ERRNZ  WORK_WIDTH-5

;/*
;** Compute the offset within the COLOR mask.
;*/

        PUSH    EBP             ; Need an extra register
        MOV     AL,chPlaneMask
        MOV     EBP,ulOffsetMask
        SHR     AL,1
        JZ      @F
        ADD     EBP,MASK_LENGTH
        SHR     AL,1
        JZ      @F
        ADD     EBP,MASK_LENGTH
        SHR     AL,1
        JZ      @F
        ADD     EBP,MASK_LENGTH
@@:
        SUB     EBP,EDI         ; Make EBP relative to destination
        JMP     EDX

;/*
;** Register usage for the loop is:
;**
;**        EDX  =  loop starting address
;**        EBX  =  offset from [EDI] to the AND mask
;**        CL   =
;**        CH   =  height
;**        EBP  =  offset from [EDI] to the COLOR mask
;**        ESI --> Source (save area)
;**        EDI --> Destination (screen area)
;*/

Color_to_screen_width_5:
        LODSB
        XOR     AL,[EBX][EDI][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]
        AND     AL,[EBX][EDI]
        OR      AL,DS:[EBP][EDI]
        STOSB
Color_to_screen_width_4:
        .ERRNZ  Color_to_screen_width_4-Color_to_screen_width_5-15
        LODSB
        XOR     AL,[EBX][EDI][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]
        AND     AL,[EBX][EDI]
        OR      AL,DS:[EBP][EDI]
        STOSB
Color_to_screen_width_3:
        .ERRNZ  Color_to_screen_width_3-Color_to_screen_width_4-15
        LODSB
        XOR     AL,[EBX][EDI][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]
        AND     AL,[EBX][EDI]
        OR      AL,DS:[EBP][EDI]
        STOSB
Color_to_screen_width_2:
        .ERRNZ  Color_to_screen_width_2-Color_to_screen_width_3-15
        LODSB
        XOR     AL,[EBX][EDI][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]
        AND     AL,[EBX][EDI]
        OR      AL,DS:[EBP][EDI]
        STOSB
Color_to_screen_width_1:
        .ERRNZ  Color_to_screen_width_1-Color_to_screen_width_2-15
        LODSB
        XOR     AL,[EBX][EDI][CURSORSTRUCT.BaseXorMasks-CURSORSTRUCT.BaseAndMasks]
        AND     AL,[EBX][EDI]
        OR      AL,DS:[EBP][EDI]
        STOSB
Color_to_screen_width_0:
        .ERRNZ  Color_to_screen_width_0-Color_to_screen_width_1-15

        SUB     EBP,SCREEN_CBSCAN
        ADD     EBP,WORK_WIDTH
        MOV     EAX,EBP                 ; EAX --> next scan's COLOR mask
        POP     EBP

;/*
;** WE HAVE NO SPARE REGISTERS HERE
;*/

        XCHG    EAX,ulOffsetSave
        ADD     AL,SAVE_BUFFER_WIDTH    ; Take into account wrap around

        MOV     ESI,Save1

        ADD     ESI,EAX                 ; ESI --> next scan's save area
        XCHG    ulOffsetSave,EAX

        ADD     EDI,ulDelScreen         ; EDI --> next scan's screen area
        SUB     EBX,SCREEN_CBSCAN
        ADD     EBX,WORK_WIDTH          ; BX --> next scan's AND mask
        DEC     CH
        JZ      @F
        PUSH    EBP
        MOV     EBP,EAX                 ; EBP --> next scan's COLOR mask
        JMP     EDX                     ; Next pass
@@:
        RET

CPSDoAPass ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE ANDIntoWork
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ANDIntoWork
;*
;* DESCRIPTION   = All rectangles which are to be ANDed into the work area are
;*                 dispatched to the routine which will do the actual ANDing.
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         = AL = fbAndRead
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** ECX passed in as pCurData
;** Doesn't need to save/restore anything
;*/

ANDIntoWork PROC SYSCALL PUBLIC

;/*
;** Program the EGA for AND mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_AND, and setting GRAF_BIT_MASK to FF.  All but setting
;** DR_AND was set by save_hw_regs.  All planes were also enabled for writing
;** by save_hw_regs.
;*/

ASSUME  ECX:PCURSORSTRUCT

        XCHG    EAX,EBX
        MOV     EDX,EGA_BASE + GRAF_ADDR
        MOV     EAX,DR_AND SHL 8 + GRAF_DATA_ROT
        OUT     DX,AX
        XCHG    EAX,EBX

;/*
;** If FB_NEW_PTR OR FB_WORK_AREA is set, then we only have a single rectangl
;** to deal with, located on the screen.
;*/

        LEA     ESI,[ECX].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[ecx].ppdNew

        TEST    AL,FB_NEW_PTR
        JNZ     AIW_source_is_screen
        LEA     ESI,[ECX].rdWork
        TEST    AL,FB_WORK_RECT
        JNZ     AIW_source_is_screen

;/*
;** Some combination of FB_READ_X, FB_READ_Y, FB_OVERLAP exists
;*/

        TEST    AL,FB_READ_Y
        JZ      @F
        LEA     ESI,[ECX].rdReadY
        INVOKE  ANDFromScreen
        MOV     AL,[ECX].fbAndRead

@@:
        TEST    AL,FB_READ_X
        JZ      @F
        LEA     ESI,[ECX].rdReadX

AIW_source_is_screen:
        INVOKE  ANDFromScreen
        MOV     AL,[ECX].fbAndRead

@@:
        TEST    AL,FB_OVERLAP
        JZ      @F
        LEA     ESI,[ECX].rdOverlap
        INVOKE  ANDFromSave

@@:
        RET

ANDIntoWork ENDP

        SUBTITLE ANDFromScreen
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ANDFromScreen
;*
;* DESCRIPTION   = The screen is ANDed with the AND mask and placed into the
;*                 work area.
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         = ESI --> RECT_DATA structure to use
;*                 EGA programmed for AND mode
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
;/*
;** ECX passed in as pCurData
;** Must save/restore ECX
;** Makes no calls
;*/

ANDFromScreen PROC SYSCALL PUBLIC USES ECX

        .ERRNZ  POINTER_DATA.rd                        ;Must be at offset 0

        MOV     EBP,ECX
ASSUME  EBP:PCURSORSTRUCT

;/*
;** Compute the screen address of this rectangle and get it's size.
;*/
        IMUL    EBX,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        ADD     EBX,[ESI].RECT_DATA.ptlScreen.ptl_x
        ADD     EBX,DS:[EBP].selScreenPtr

;/*
;**EBX points to the rectangle in VRAM
;*/

        MOVZX   ECX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
;/*
;** CH is cy
;** CL is cx
;*/

;/*
;** Compute the offset from the start of the work area and the AND mask (it's
;** the same value).
;*/

        XOR     EAX,EAX
        MOVZX   EDX,[ESI].RECT_DATA.ptbWork     ; Get origin in work area
        XCHG    AL,DH                           ; EAX = y, DX = x
        ADD     EDX,EAX                         ; EDX = y *1 + X component
        SHL     EAX,2                           ; EAX = y * 4
        ADD     EAX,EDX                         ; EAX = y * 5 + X = start
                                                ;       from work/mask
        XCHG    EAX,EDI                         ; EDI = start from work/mask
        .ERRNZ  WORK_WIDTH-5

;/*
;** To save incrementing the source pointer (EBX), subtract the AND mask
;** pointer off of it.  Then use [EBX][ESI] for addressing into the source.
;** As ESI is incremented, EBX will effectively be incremented.
;**
;** The source pointer will have to be adjusted by SCREEN_CBSCAN-SIZEB.sizb_cx.
;*/

        MOV     ESI,DS:[EBP].pAndXor                     ; Set address of AND mask
        ADD     ESI,EDI                         ; ESI --> start of AND mask
        ADD     EDI,DS:[EBP].selScreenPtr
        ADD     EDI,abPointerWork         ; EDI --> start of rectangle
                                                ;         in work area

;/*
;** EBX points to the rectangle in VRAM
;*/

        SUB     EBX,ESI                         ; EBX --> start of source
                                                ;         relative to [ESI]

;/*
;** Compute the delta to the start of the next scanline of the AND mask and
;** the work area, and the next scan of the screen.
;*/

        MOVZX   EAX,CL
        MOV     EBP,WORK_WIDTH
ASSUME  EBP:NOTHING
        SUB     EBP,EAX
        MOV     EDX,SCREEN_CBSCAN
        SUB     EDX,WORK_WIDTH
        SHL     EAX,2
        NEG     EAX

        PUSH    EBX
        Call    GetEIPinEBX
Start3:
        ADD     EBX,ANDFromScreen_width_0-Start3

;/*
;** EBX now has absolute address of ANDFromScreen_width_0
;*/

        ADD     EAX,EBX
        POP     EBX

        JMP     EAX                             ; Enter following code
        .ERRNZ  WORK_WIDTH-5
        .ERRNZ  ANDFromScreen_width_0-ANDFromScreen_width_1-4
        .ERRNZ  ANDFromScreen_width_1-ANDFromScreen_width_2-4
        .ERRNZ  ANDFromScreen_width_2-ANDFromScreen_width_3-4
        .ERRNZ  ANDFromScreen_width_3-ANDFromScreen_width_4-4
        .ERRNZ  ANDFromScreen_width_4-ANDFromScreen_width_5-4

;/*
;** Register usage for the loop will be:
;**
;**        EAX  =  Loop starting address
;**        EBX  =  Offset from ESI to the screen source
;**        CL   =
;**        CH   =  Height
;**        EDX  =  Delta to next scan of the screen source
;**        ESI --> AND mask
;**        EDI --> Destination in work area
;**        EBP  =  Offset to next byte of AND mask, next scan of work area
;*/

ANDFromScreen_width_5:
        CMP     AL,[EBX][ESI]           ; Load latches from work area
        MOVSB                           ; AND from screen into work area
ANDFromScreen_width_4:
        CMP     AL,[EBX][ESI]
        MOVSB
ANDFromScreen_width_3:
        CMP     AL,[EBX][ESI]
        MOVSB
ANDFromScreen_width_2:
        CMP     AL,[EBX][ESI]
        MOVSB
ANDFromScreen_width_1:
        CMP     AL,[EBX][ESI]
        MOVSB
ANDFromScreen_width_0:
        ADD     ESI,EBP                 ; ESI --> next AND mask
        ADD     EDI,EBP                 ; EDI --> next destination
        ADD     EBX,EDX                 ; EBX --> next source
        DEC     CH
        JZ      @F
        JMP     EAX                     ; Next pass

@@:
        RET
ANDFromScreen ENDP

        SUBTITLE ANDFromSave
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ANDFromSave
;*
;* DESCRIPTION   = The given area of the save buffer is ANDed into the work
;*                 buffer.
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         = ESI --> RECT_DATA structure to use
;*                 EGA programmed for AND mode
;*
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** Doesn't need to save/restore pCurData
;** Doesn't call anything
;** ECX passed in as pCurData
;*/

ANDFromSave PROC SYSCALL PUBLIC

        PUSH    ECX     ;SavePtr on top of stack
;/*
;** This is the key to wrapping in the buffer.  It is a power of two, and in
;** this case the entire size is 256 bytes.  If it wasn't 256 bytes, an AND
;** mask could be used for wrapping.
;*/

        .ERRNZ  SAVE_BUFFER_WIDTH-8
        .ERRNZ  SAVE_BUFFER_HEIGHT-32
        .ERRNZ  SAVE_BUFFER_WIDTH*SAVE_BUFFER_HEIGHT-256
        .ERRNZ  POINTER_DATA.rd

;/*
;** See if we'll wrap in X.  If so, split the operation into two parts
;*/

        MOVZX   EAX,[ESI].RECT_DATA.ptbWork
        MOVZX   ECX,[ESI].RECT_DATA.ptbSave
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
        MOVZX   EDX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        MOV     BL,CL
        ADD     BL,DL
        SUB     BL,SAVE_BUFFER_WIDTH             ; BL = is overhang
        JLE     ANDFromSave_do_a_pass            ; No overhang

        SUB     DL,BL                            ; Set X extent for pass 1
        MOV     BH,DL                            ; Need it for pass 2
        PUSH    ESI                              ; Must keep rectangle pointer around
        PUSH    EBX
        MOV     EBX,DWORD PTR [ESP+8]            ;SavePtr
        CALL    ANDFromSave_do_a_pass_entry      ; Process first half
        POP     EBX
        POP     ESI

;/*
;** SavePtr back on top of stack
;*/

        MOVZX   EAX,[ESI].RECT_DATA.ptbWork
        MOVZX   ECX,[ESI].RECT_DATA.ptbSave
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
        MOVZX   EDX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1

        ADD     AL,BH                            ; Move right in work area
        MOV     DL,BL                            ; Set new extent
        XOR     CL,CL                            ; X origin in save area is 0
        JMP     ANDFromSave_do_a_pass

ANDFromSave_do_a_pass_entry:
        PUSH    EBX

ANDFromSave_do_a_pass:  ;SavePtr is on top of stack

;/*
;** Compute the offset within the work area, which is also the offset from
;** the start of the AND mask.
;*/

        MOVZX   EBX,AH
        MOVZX   EAX,AL
        ADD     EAX,EBX                 ; *1 + x
        SHL     EBX,2                   ; *4
        ADD     EBX,EAX                 ; *5 + x

        Mov     ESI,DWORD PTR [ESP]     ;ESI=SavePtr
ASSUME  ESI:PCURSORSTRUCT
        LEA     EDI,[EBX+abPointerWork]
        ADD     EDI,[ESI].selScreenPtr  ; EDI --> destination in work area
        MOV     ESI,[ESI].pAndXor
ASSUME  ESI:NOTHING
        ADD     ESI,EBX                 ; ESI --> AND mask

;/*
;** Compute the offset within the save area.  Note that this is relative
;** to screen_buf.
;*/

        ; BX = ptbSave.ptb_y, CX = ptbSave.ptb_x
        MOVZX   EBX,CH
        MOVZX   ECX,CL

        SHL     EBX,3
        ADD     ECX,EBX         ; ECX = offset in save area
        .ERRNZ  SAVE_BUFFER_WIDTH-8


;/*
;** Compute the adjustment to each scanline.
;*/

        MOV     EBX,SAVE_BUFFER_WIDTH
        XCHG    BL,DL
        NEG     EBX                     ; -width
        LEA     EAX,[EBX][WORK_WIDTH]

;/*
;** Compute the adress of where to enter the AND loop
;*/

        ADD     EBX,EBX                 ; *-2
        ADD     EBX,EBX                 ; *-4
        PUSH    EAX
        Call    GetEIPinEAX
Start4:
        ADD     EAX,ANDFromSave_width_0-Start4

;/*
;** EBX now has absolute address of ANDFromSave_width_0
;*/

        ADD     EBX,EAX
        POP     EAX
        .ERRNZ  ANDFromSave_width_0-ANDFromSave_width_1-4
        .ERRNZ  ANDFromSave_width_1-ANDFromSave_width_2-4
        .ERRNZ  ANDFromSave_width_2-ANDFromSave_width_3-4
        .ERRNZ  ANDFromSave_width_3-ANDFromSave_width_4-4
        .ERRNZ  ANDFromSave_width_4-ANDFromSave_width_5-4
        .ERRNZ  WORK_WIDTH-5


;/*
;** Now get things into the correct registers and enter the loop
;*/

        XCHG    EBX,ECX                         ; EBX = Offset in save area
                                                ; ECX --> Loop entry point

;/*
;** SavePtr on top of stack
;*/
        push    ecx
        MOV     ecx,DWORD PTR [ESP+4]           ;ECX=SavePtr
        LEA     EBP,[abPointerSave+EBX]                  ; Set correct save buffer
                                                ; address
        ADD     EBP,[ECX].selScreenPtr
        SUB     EBP,ESI                         ; Want to use [ESI][EBP]
        POP     ECX

;/*
;** SavePtr back on top of stack
;*/

        JMP     ECX                             ; Enter following code

;/*
;** Register usage for the loop will be:
;**
;**        EAX  =  Delta to next scan of AND mask and work area
;**        EBX  =  offset in save area of scan start
;**        ECX  =  loop address
;**        DL   =  SAVE_BUFFER_WIDTH
;**        DH   =  height
;**        ESI --> AND mask
;**        EDI --> Destination in work area
;**        EBP --> Source in save area relative to [ESI]
;*/

ANDFromSave_width_5:
        CMP     AL,DS:[EBP][ESI]                 ; Load latches from screen
        MOVSB                                    ; AND to the work area
ANDFromSave_width_4:
        CMP     AL,DS:[EBP][ESI]
        MOVSB
ANDFromSave_width_3:
        CMP     AL,DS:[EBP][ESI]
        MOVSB
ANDFromSave_width_2:
        CMP     AL,DS:[EBP][ESI]
        MOVSB
ANDFromSave_width_1:
        CMP     AL,DS:[EBP][ESI]
        MOVSB
ANDFromSave_width_0:
        DEC     DH
        JZ      ANDFromSave_done
        ADD     ESI,EAX                         ; ESI --> next AND mask
        ADD     EDI,EAX                         ; EDI --> next save buf scan
        ADD     BL,DL                           ; Compute address of next

;/*
;** SavePtr on top of stack
;*/

        PUSH    EDI
        MOV     EDI,[ESP+4]     ;SavePtr
ASSUME  EDI:PCURSORSTRUCT
        LEA     EBP,[abPointerSave+EBX]                  ; scan in the work area
        ADD     EBP,[EDI].selScreenPtr
        SUB     EBP,ESI                         ; Want to use ESI for
                                                ; incrementing
        POP     EDI
ASSUME  EDI:NOTHING

;/*
;** SavePtr back on top of stack
;*/

        JMP     ECX

ANDFromSave_done:
        POP     ECX
        RET

ANDFromSave ENDP

        SUBTITLE CopyThingsAround
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = CopyThingsAround
;*
;* DESCRIPTION   = Rectangles to be copied from the save area to the screen
;*                 are processed, followed by the rectangles to be copied
;*                 from the screen to the save area
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         = AL = fbFlush
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** ECX passed in as pCurData
;** No need to save/restore ECX
;** Makes calls
;*/

CopyThingsAround PROC SYSCALL PUBLIC
        MOV     EDI,ECX
ASSUME  EDI:PCURSORSTRUCT

;/*
;** Program the EGA for COPY mode.  This will be done using M_PROC_WRITE,
;** M_DATA_READ, DR_???, and setting GRAF_BIT_MASK to 00.  All but setting
;** GRAF_BIT_MASK was done by save_hw_regs.  All planes were also enabled for
;** writing by save_hw_regs.
;*/

        XCHG    EAX,ECX
        MOV     EDX,EGA_BASE + GRAF_ADDR
        MOV     EAX,GRAF_BIT_MASK
        OUT     DX,AX   ; Disable all bits
        XCHG    EAX,ECX

;/*
;** Process any copying from the save area to the screen.  If FB_OLD_PTR is
;** set, then there cannot be a FB_FLUSH_X OR FB_FLUSH_Y.
;*/

        LEA     ESI,[EDI].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[EDI].ppdOld

        TEST    AL,FB_OLD_PTR
        JNZ     CTA_copy_from_save      ; The old rectangle goes
        TEST    AL,FB_FLUSH_X
        JZ      @F
        LEA     ESI,[EDI].rdFlushX
        INVOKE  CopySaveToScreen
        MOV     AL,[EDI].fbFlush
@@:
        TEST    AL,FB_FLUSH_Y
        JZ      @F
        LEA     ESI,[EDI].rdFlushY

CTA_copy_from_save:
        INVOKE  CopySaveToScreen
@@:

;/*
;** Process any copying from the screen to the save area.  If FB_NEW_PTR or
;** FB_WORK_RECT is set, then there can't be a FB_FLUSH_X OR FB_FLUSH_Y.
;*/

        MOV     AL,[EDI].fbAndRead
        LEA     ESI,[EDI].pdPtr0              ; ESI --> new POINTER_DATA structure
        ADD     ESI,[EDI].ppdNew

        TEST    AL,FB_NEW_PTR
        JNZ     CTA_copy_to_save
        LEA     ESI,[EDI].rdWork
        TEST    AL,FB_WORK_RECT
        JNZ     CTA_copy_to_save

;/*
;** Some combination of FB_READ_X, FB_READ_Y
;*/

        TEST    AL,FB_READ_X
        JZ      @F
        LEA     ESI,[EDI].rdReadX
        INVOKE  CopyScreenToSave
        MOV     AL,[EDI].fbAndRead

@@:
        TEST    AL,FB_READ_Y
        JZ      @F
        LEA     ESI,[EDI].rdReadY

CTA_copy_to_save:
        INVOKE  CopyScreenToSave

@@:
        RET

CopyThingsAround ENDP

        SUBTITLE CopySaveToScreen
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = CopySaveToScreen
;*
;* DESCRIPTION   = The given rectangle is copied from the save area to the
;*                 screen.
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         = ESI --> RECT_DATA structure to use
;*                 EGA programmed for COPY mode
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** pCurData in EDI
;** makes no calls
;** Must save/restore EDI
;*/

CopySaveToScreen PROC SYSCALL PUBLIC

        PUSH    EDI     ;SavePtr on top of stack
;/*
;** This is the key to wraping in the buffer.  It is a power of two, and in
;** this case the entire size is 256 bytes.  If it wasn't 256 bytes, an AND
;** mask could be used for wrapping.
;*/

        .ERRNZ  SAVE_BUFFER_WIDTH-8
        .ERRNZ  SAVE_BUFFER_HEIGHT-32
        .ERRNZ  SAVE_BUFFER_WIDTH*SAVE_BUFFER_HEIGHT-256
        .ERRNZ  POINTER_DATA.rd

;/*
;** Compute the screen address and delta to next scan of the screen
;*/

        IMUL    EDI,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        ADD     EDI,[ESI].RECT_DATA.ptlScreen.ptl_x
        MOV     EDX,DWORD PTR [ESP]           ;SavePtr
        ADD     EDI,[EDX].CURSORSTRUCT.selScreenPtr        ; EDI --> Source in screen area
        MOV     DX,WORD PTR [ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        MOVZX   EAX,DL
        MOV     EBP,SCREEN_CBSCAN
        SUB     EBP,EAX                 ; EBP = delta to next scan of screen

;/*
;** Compute the save area offset.
;*/

        MOVZX   ECX,[ESI].RECT_DATA.ptbSave
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1

;/*
;** CH = 0 for looping later
;*/

        MOVZX   EBX,CH
        MOVZX   ECX,CL

        SHL     EBX,3
        .ERRNZ  SAVE_BUFFER_WIDTH-8
        ADD     EBX,ECX

;/*
;** Determine if any wrap will occur, and handle it if so.
;*/

        MOV     EAX,SAVE_BUFFER_WIDTH
        ADD     CL,DL                            ; Add extent (DL) to start X (CL)
        SUB     CL,AL
        JG      Save_to_screen_wraps             ; CL = amount of wrap

;/*
;** The copy will not wrap, so we can get into some tighter code for it.
;**
;** Currently:
;**        AL   =  SAVE_BUFFER_WIDTH
;**        DH   =  Height of copy
;**        DL   =  Width of copy
;**        EBP  =  Delta to next destination scan
;**        EBX  =  Offset into the save buffer
;**        EDI --> Destination
;*/

Copy_save_next_scan:
        MOV     ECX,DWORD PTR [ESP]     ;SavePtr
ASSUME  ECX:PCURSORSTRUCT
        LEA     ESI,[abPointerSave+EBX]
        ADD     ESI,[ECX].selScreenPtr
ASSUME  ECX:NOTHING
        MOVZX   ECX,DL
    REP MOVSB
        ADD     BL,AL
        ADD     EDI,EBP
        DEC     DH
        JNZ     Copy_save_next_scan
        JMP     CSTS_end

;/*
;** The copy will wrap, so we have to handle it as two copies
;**
;** Currently:
;**        EDI --> Destination
;**        EBP  =  Delta to next destination scan
;**        DL   =  Width of copy
;**        DH   =  Height of copy
;**        EBX  =  Offset into the save buffer
;**        AL   =  SAVE_BUFFER_WIDTH
;**        CL   =  Amount of overflow
;*/

Save_to_screen_wraps:
        SUB     DL,CL                   ; Set extent of first copy
        MOV     AH,CL                   ; Set extent of second copy

Copy_save_next_scan_wrap:
        MOV     ECX,DWORD PTR [ESP]     ;SavePtr
ASSUME  ECX:PCURSORSTRUCT
        LEA     ESI,[abPointerSave+EBX]
        ADD     ESI,[ECX].selScreenPtr
ASSUME  ECX:NOTHING
        MOVZX   ECX,DL
    REP MOVSB
        SUB     ESI,SAVE_BUFFER_WIDTH
        MOVZX   ECX,AH
    REP MOVSB
        ADD     BL,AL
        ADD     EDI,EBP
        DEC     DH
        JNZ     Copy_save_next_scan_wrap

CSTS_end:
        POP     EDI
        RET

CopySaveToScreen ENDP

        SUBTITLE CopyScreenToSave
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = CopyScreenToSave
;*
;* DESCRIPTION   = The given rectangle is copied from the screen to the save area.
;*
;*                 Registers Preserved:
;*                       DS,ES
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,BP,FLAGS
;*
;* INPUT         =  SI --> RECT_DATA structure to use
;*                  EGA programmed for COPY mode
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** pCurData in EDI
;** makes no calls
;** Must save/restore EDI
;*/

CopyScreenToSave PROC SYSCALL PUBLIC

        PUSH    EDI     ;SavePtr on top of stack
;/*
;** This is the key to wraping in the buffer.  It is a power of two, and in
;** this case the entire size is 256 bytes.  If it wasn't 256 bytes, an and
;** mask could be used for wrapping.
;*/

        .ERRNZ  SAVE_BUFFER_WIDTH-8
        .ERRNZ  SAVE_BUFFER_HEIGHT-32
        .ERRNZ  SAVE_BUFFER_WIDTH*SAVE_BUFFER_HEIGHT-256
        .ERRNZ  POINTER_DATA.rd

;/*
;** Compute the screen address and delta to next scan of the screen
;*/

        IMUL    EDI,[ESI].RECT_DATA.ptlScreen.ptl_y,SCREEN_CBSCAN
        ADD     EDI,[ESI].RECT_DATA.ptlScreen.ptl_x
        MOV     EDX,DWORD PTR [ESP]     ;SavePtr
        ADD     EDI,[EDX].CURSORSTRUCT.selScreenPtr
        MOVZX   EDX,[ESI].RECT_DATA.sizb
        .ERRNZ  SIZEB.sizb_cy-SIZEB.sizb_cx-1
        MOVSX   EAX,DL
        MOV     EBP,SCREEN_CBSCAN
        SUB     EBP,EAX

;/*
;** Compute the save area offset
;*/

        MOVZX   ECX,[ESI].RECT_DATA.ptbSave
        .ERRNZ  POINTB.ptb_y-POINTB.ptb_x-1
        XOR     EBX,EBX
        XCHG    BL,CH           ; CH = 0 for looping later
        SHL     EBX,3
        .ERRNZ  SAVE_BUFFER_WIDTH-8
        ADD     EBX,ECX

;/*
;** Determine if any wrap will occur, and handle it if so
;*/

        XCHG    EDI,ESI                 ; It helps to have the source in ESI
        MOV     EAX,SAVE_BUFFER_WIDTH
        ADD     CL,DL                   ; Add extent (DL) to start X (CL)
        SUB     CL,AL                   ; Width of the save buffer
        JG      Screen_to_save_wraps             ; CL = amount of wrap

;/*
;** The copy will not wrap, so we can get into some tighter code for it.
;**
;** Currently:
;**        EAX  =  SAVE_BUFFER_WIDTH
;**        CH   =  0
;**        DH   =  Height of copy
;**        DL   =  Width of copy
;**        EBP  =  Delta to next source scan
;**        EBX  =  Offset into the save buffer
;**        ESI --> Source
;*/

Copy_screen_next_scan:
        MOV     ECX,DWORD PTR [ESP]     ;SavePtr
        LEA     EDI,[abPointerSave+EBX]
        ADD     EDI,[ECX].CURSORSTRUCT.selScreenPtr
        MOVZX   ECX,DL
    REP MOVSB
        ADD     BL,AL
        ADD     ESI,EBP
        DEC     DH
        JNZ     Copy_screen_next_scan
        JMP     Sc2Sa_end

;/*
;** The copy will wrap, so we have to handle it as two copies
;**
;** Currently:
;**        ESI --> Source
;**        EBP  =  Delta to next source scan
;**        DL   =  Width of copy
;**        DH   =  Height of copy
;**        CH   =  0
;**        CL   =  Amount of overflow
;**        EBX  =  Offset into the save buffer
;**        AL   =  SAVE_BUFFER_WIDTH
;*/

Screen_to_save_wraps:
        SUB     DL,CL                           ; Set extent of first copy
        MOV     AH,CL                           ; Set extent of second copy

Copy_screen_next_scan_wrap:
        MOV     ECX,DWORD PTR [ESP]     ;SavePtr
        LEA     EDI,[abPointerSave+EBX]
        ADD     EDI,[ECX].CURSORSTRUCT.selScreenPtr
        MOVZX   ECX,DL
    REP MOVSB
        SUB     EDI,SAVE_BUFFER_WIDTH
        MOVZX   ECX,AH
    REP MOVSB
        ADD     BL,AL
        ADD     ESI,EBP
        DEC     DH
        JNZ     Copy_screen_next_scan_wrap

Sc2Sa_end:
        POP     EDI
        RET

CopyScreenToSave ENDP
page

;/***************************************************************************
;*
;* FUNCTION NAME = save_hw_regs
;*
;* DESCRIPTION   =
;*          This routine is called by the pointer shape drawing code whenever
;*          the state of the EGA registers must be save.  The contents of the
;*          following registers are saved:
;*
;*                Map Mask
;*                Enable Set/Reset
;*                Data Rotate
;*                Read Map Select
;*                Bit Mask
;*                Processor Latches
;*
;*          The pointer shape drawing routine must call RES_HW_REGS to restore
;*          the registers and prepare the internal work areas for the next call
;*          to this routine.
;*
;*          Registers Destroyed:
;*                AX,BX,CX,DX,FLAGS
;*                GRAF_ADDR                                   (EGA register)
;*          Registers Preserved:
;*                SI,DI,BP,DS,ES
;*                SEQ_ADDR                                    (EGA register)
;*
;*
;* INPUT         = DS              = Data segment selector
;*                 ES              = EGA  memory  selector
;*
;* OUTPUT        = GRAF_MODE       = M_DATA_READ+M_PROC_WRITE (EGA register)
;*                 GRAF_ENAB_SR    = all planes disabled      (EGA register)
;*                 GRAF_BIT_MASK   = all bits enabled         (EGA register)
;*                 GRAF_DATA_ROT   = DR_SET                   (EGA register)
;*                 SEQ_MAP_MASK    = all planes enabled       (EGA register)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*
;*                        -----Pseudo-Code---
;*
;*      New EGA Shadow Specification
;*
;*      The hypothesis is that there is a method wherein the state
;*      of the EGA registers used by the Color EGA Drivers can be
;*      determined at interrupt time, with minimum limitations on
;*      the display driver.
;*
;*      The registers used by the current color drivers are:
;*
;*          Sequencer:                               Map Mask
;*
;*          Graphics Controller:    Set/Reset
;*                                                   Enable Set/Reset
;*                                                   Color Compare
;*                                                   Data Rotate
;*                                                   Read Map Select
;*                                                   Mode
;*                                                   Color Don't Care
;*                                                   Bit Mask
;*
;*
;*      Of these registers, the following are currently shadowed for
;*      the interrupt cursor drawing code:
;*
;*          Sequencer:                               Map Mask
;*
;*          Graphics Controller:    Enable Set/Reset
;*                                                   Data Rotate
;*                                                   Mode
;*                                                   Bit Mask
;*
;*
;*
;*      By following the prescribed algorithm, the following registers
;*      could be saved and restored at interrupt time:
;*
;*          Sequencer:                               Map Mask
;*
;*          Graphics Controller:    Enable Set/Reset
;*                                                   Data Rotate
;*                                                   Read Map Select
;*                                                   Bit Mask
;*
;*          Processor Latches
;*
;*
;*      The limitations proposed are:
;*
;*          1)  The driver must indicate if it is in Data Read mode or
;*              Color Compare Read mode.  The driver must indicate the
;*              write mode in use.  These values are part of the same
;*              register, so only one register need be shadowed.
;*
;*
;*      The process:
;*
;*          1)  Place the EGA into Write Mode 1.  In this mode, the
;*              contents of the EGA's latches are written to memory.
;*              Since the Read Mode is part of the same byte, it
;*              will be set to Data Read mode, which will be required
;*              by the state detection code.
;*
;*
;*          2)  Determine the state of the Map Mask Register.
;*              This is accomplished by:
;*
;*              A)  Write to two locations in EGA memory where the
;*                  bytes are known to be different in the same
;*                  plane.  These locations will be maintained by
;*                  the cursor code and EGA initialization code in
;*                  this state.
;*
;*              B)  If the bytes are the same for a given plane, then
;*                  that plane was enabled for writing.  If the bytes
;*                  are different for a given plane, then that plane
;*                  was not enabled for writing.
;*
;*              The Map Mask Register may now be altered.
;*
;*
;*          3)  Save the EGA Processor Latches.  This is accomplished
;*              by:
;*
;*                  A)  Enabling all planes for writing.
;*
;*                  B)  Writing to a predefined location in video RAM
;*                      in Write Mode 1.
;*
;*              This location (in all four planes) now contains the
;*              contents of the Processor Latches at the time of the
;*              interrupt.
;*
;*              The Processor Latches may now be altered.
;*
;*
;*          4)  Determined the Read Map Select Register.  Since the
;*              EGA has already been placed into Data Read mode,
;*              simply read from a predefined location.  Each plane
;*              of this location has the plane index value in it.
;*              The value returned will be the Read Map Select index.
;*
;*              The Read Map Select Register may now be altered.
;*
;*
;*          5)  Determine the state of the Bit Mask Register and the
;*              Data Rotate Register.  Since the EGA driver doesn't
;*              use the Rotate Count, only the boolean function of
;*              the Data Rotate register need be determined.  This is
;*              accomplished by the following:
;*
;*                  A)  Enable Write Mode 2.  In Write Mode 2, the
;*                      Enable Set/Reset Register and the Rotate
;*                      Count are ignored.  Any host data is taken to
;*                      be a color, with 8 bits of D0 being written
;*                      to C0, and so forth for the other planes.
;*
;*                  B)  Perform two writes (XCHGs) to EGA memory in
;*                      a location where the planes are defined as
;*                      follows:
;*
;*                                               C3 C2 C1 C0
;*
;*                                               FF FF 00 00
;*
;*                      The first write consists of writing 00 to
;*                      planes C0 and C2.  The second write consists
;*                      of writing FF to planes C1 and C3.
;*
;*                  C)  By interpreting the results, the value of the
;*                      Bit Mask Register and the Data Rotate Register
;*                      can be determined:
;*
;*                                               C3 C2 C1 C0
;*
;*                                               FF FF 00 00     Bit Mask is 0
;*                                               FF 00 FF 00     Mode is SET
;*                                               FF FF FF 00     Mode is OR
;*                                               FF 00 00 00     Mode is AND
;*                                               00 FF FF 00     Mode is XOR
;*
;*                      Anywhere that the result is the original value,
;*                      the Bit Mask Register was 0.  If all bits show
;*                      the original value, then the Bit Mask Register
;*                      was 0 in all bits, and the test must be rerun
;*                      with the Bit Mask enabled for at least one bit
;*                      (this will be the case when copying from screen
;*                      to screen).
;*
;*                      Note that C0 never participates in the decision.
;*
;*                      Any '1' bit in C1 indicates a bit which changed.
;*                      Any '0' bit in C2 or C3 indicates a bit which
;*                      changed.
;*
;*
;*          6)  Determine the state of the Enable Set/Reset Register.
;*              This is done by the following:
;*
;*                  A)  Enable all bits of the Bit Mask Register.
;*
;*                  B)  Enable writing to all planes.
;*
;*                  C)  Set Write Mode 0.  Enable Set/Reset is
;*                      recognized in Write Mode 0.
;*
;*                  D)  Set Data Rotate Function Select to SET.
;*
;*                  E)  Write 55h to a location.
;*
;*                  F)  Any plane of the location where the result
;*                      isn't 55h was enabled for Set/Reset.
;*
;*
;*      The state of the registers has now been determined.
;*
;* NULL save_hw_regs()
;* {
;*   perform steps 1 thru 6;
;*   GRAF_ENAB_SR = all planes disabled;
;*   return();
;* }
;*
;**************************************************************************/

;/*
;**
;** VGA Version of save_hw_regs
;**
;**
;** pCurData in ECX
;** No need to save/restore ECX
;** makes no calls
;*/

save_hw_regs PROC SYSCALL PUBLIC USES EDI
ASSUME ECX:PCURSORSTRUCT
        mov     edi,[ecx].selScreenPtr

;/*
;** Preload with something useful
;*/

        mov     edx, EGA_BASE
;/*
;** When saving the latch register need to ensure that all the
;** planes are enabled, so save the map mask register
;*/

        mov     dl, SEQ_ADDR
        in      al, dx                                          ;          
        mov     [ecx].vga_seq_indx, al                          ;          
        mov     al, SEQ_MAP_MASK
        out     dx, al
        mov     dl, SEQ_DATA
        in      al, dx
        mov     [ecx].vga_seq_map_mask, al

;/*
;** Enable all of the planes
;*/

        mov     al, MM_ALL
        out     dx, al
;/*
;** Need to change the mode to write the processor latches so save
;** the mode register
;*/

        mov     dl, GRAF_ADDR                     ;             55847
        in      al, dx                            ;             55847

        mov     [ecx].vga_grf_indx,al
        mov     al, GRAF_MODE
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_mode, al
;/*
;** Set the write mode to 1 so that the latches are copied on the
;** next write to display memory
;*/

        mov     al, M_LATCH_WRITE
        out     dx, al
;/*
;** Save a dummy value into the display memory, this actually
;** copies the processor latches to memory where they can be
;** restored later on
;*/

ifdef EGAMEM_IS_STRUCT
        mov     [EDI].EGAMem.saved_latches,al
else
        mov     [EDI+saved_latches],al
endif

;/*
;** Leave the mode in a known state for the cursor code.
;*/

        mov     al, M_PROC_WRITE
        out     dx, al

;/*
;** Save the rest of the graphics controller registers
;*/

        mov     dl, GRAF_ADDR
        mov     al, GRAF_SET_RESET
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_set, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_ENAB_SR
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_enable_set, al

;/*
;** Leave the enable set/reset in a known state for the cursor code.
;*/

        mov     al, 0                  ; Leave set/reset at zero
        out     dx, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_COL_COMP
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_color_cmp, al
;/*
;** Having saved the data rotate register reset any rotate bits
;** and the function select
;*/

        mov     dl, GRAF_ADDR
        mov     al, GRAF_DATA_ROT
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_rotate, al
;/*
;** Leave the data rotate and function flags in a known state for
;** the cursor code.
;*/

        mov     al, DR_SET
        out     dx, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_READ_MAP
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_read_map, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_MISC
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_misc, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_CDC
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_color, al

        mov     dl, GRAF_ADDR
        mov     al, GRAF_BIT_MASK
        out     dx, al
        mov     dl, GRAF_DATA
        in      al, dx
        mov     [ecx].vga_grf_bitmask, al


;/*
;** Leave the bitmask in a known state for the cursor code.
;*/


        mov     al, 0FFH               ; leave all bits enabled
        out     dx, al

        ret
save_hw_regs   endp

page

;/***************************************************************************
;*
;* FUNCTION NAME = res_hw_regs
;*
;* DESCRIPTION   =
;*           Restore Hardware Video Registers
;*
;*           This routine is called by the pointer shape drawing code whenever
;*           the state of the EGA registers is to be restored.  The contents of
;*           the following registers are restored:
;*
;*                 Map Mask
;*                 Enable Set/Reset
;*                 Data Rotate
;*                 Read Map Select
;*                 Bit Mask
;*                 Mode
;*                 Processor Latches
;*
;*           The pointer shape drawing routine must have previously called
;*           SAVE_HW_REGS to save the registers in the work areas from which
;*           this routine restores them.
;*
;*           The internal work areas used by SAVE_HW_REGS will be reset for the
;*           next call to SAVE_HW_REGS.
;*
;*           Registers Destroyed:
;*                 AX,CX,DX,FLAGS
;*           Registers Preserved:
;*                 BX,SI,DI,BP,DS,ES,Carry
;*                 SEQ_ADDR                                    (EGA register)
;*
;* INPUT         =  DS              = Data segment selector
;*                  ES              = EGA  memory  selector
;*                  SEQ_ADDR        = SEQ_MAP_MASK              (EGA register)
;*                  SEQ_MAP_MASK    = all planes enabled        (EGA register)
;*                  GRAF_MODE       = M_DATA_READ+M_PROC_WRITE  (EGA register)
;*                  GRAF_ENAB_SR    = all planes disabled       (EGA register)
;*
;*
;*
;*
;* OUTPUT        = GRAF_ADDR       = GRAF_BIT_MASK           (EGA register)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                       -------Pseudo-Code--
;* NULL res_hw_regs()
;* {
;*   GRAF_DATA_ROT       = DR_SET;                    // must use DR_SET mode
;*   GRAF_BIT_MASK       = 0xFF;                      // must alter all bits
;*   set_test_locs;                                   // init locations for next time
;*   SEQ_MAP_MASK        = saved_map_mask;            // restore saved registers
;*   GRAF_READ_MAP       = saved_read_map;
;*   GRAF_DATA_ROT       = saved_data_rot;
;*   GRAF_ENAB_SR        = saved_enab_sr;
;*   GRAF_MODE           = shadowed_graf_mode;
;*   GRAF_BIT_MASK       = saved_bit_mask;            // must be last written
;*   return();                                        //  to GRAF_ADDR
;* }
;*
;**************************************************************************/



;/*
;**
;** VGA Version of res_hw_regs
;**
;**
;** pCurData in ECX
;** No need to save/restore ECX
;** makes no calls
;*/

res_hw_regs PROC SYSCALL PUBLIC USES EDI
ASSUME ECX:PCURSORSTRUCT
        mov     edi, [ecx].selScreenPtr

;/*
;** Preload with something useful
;*/

        mov     edx, EGA_BASE

;/*
;** Set the write mode to 1 so that the latches are copied by
;** the next read from display memory
;*/

        mov     dl, GRAF_ADDR
        mov     ax, M_LATCH_WRITE shl 8 + GRAF_MODE
        out     dx, ax

;/*
;** Enable all of the planes so that the full 32 bit latch is
;** loaded.
;*/

        mov     dl, SEQ_ADDR
        mov     ax, MM_ALL shl 8 + SEQ_MAP_MASK
        out     dx, ax


;/*
;** Restore the latches
;*/

ifdef EGAMEM_IS_STRUCT
        mov     al,[EDI].EGAMem.saved_latches
else
        mov     al,[EDI+saved_latches]
endif

;/*
;** Restore the graphics controller registers
;*/

        mov     dl, GRAF_ADDR

        mov     al, GRAF_SET_RESET
        mov     ah, [ecx].vga_grf_set
        out     dx, ax

        mov     al, GRAF_ENAB_SR
        mov     ah, [ecx].vga_grf_enable_set
        out     dx, ax

        mov     al, GRAF_COL_COMP
        mov     ah, [ecx].vga_grf_color_cmp
        out     dx, ax

        mov     al, GRAF_DATA_ROT
        mov     ah, [ecx].vga_grf_rotate
        out     dx, ax

        mov     al, GRAF_READ_MAP
        mov     ah, [ecx].vga_grf_read_map
        out     dx, ax

        mov     al, GRAF_MODE
        mov     ah, [ecx].vga_grf_mode
        out     dx, ax

        mov     al, GRAF_CDC
        mov     ah, [ecx].vga_grf_color
        out     dx, ax

        mov     al, GRAF_BIT_MASK
        mov     ah, [ecx].vga_grf_bitmask
        out     dx, ax


;/*
;** Restore the sequencer mode
;*/

        mov     dl, SEQ_ADDR
        mov     al, SEQ_MAP_MASK
        mov     ah, [ecx].vga_seq_map_mask
        out     dx, ax

        mov     al, [ecx].vga_grf_misc
        and     al, MS_NON_ALPHA+MS_ADDR_MASK
        cmp     al, MS_NON_ALPHA+MS_A0000_64K
        je      res_hw_exit             ;GDC Misc fixup required?

;/*
;** Start of GDC Misc fixup
;*/
        mov     dl, SEQ_ADDR
        mov     ax, 0100h               ;clear synchronous reset bit
        out     dx, ax

;/*
;** FIX for timing problem on IBM VGA
;** bypass trashing "mystery reg" 7
;** for these chipsets
;*/

        cmp     [ecx].vga_svgatype, VIDEO7_ADAPTER
        je      @F
        cmp     [ecx].vga_svgatype, TSENG_ADAPTER
        je      @F

        mov     ax, 0007h               ;select "mystery reg" 7 of sequencer
        out     dx, ax                  ;and write a 0 to it
        mov     ax, 013Fh               ;select "mystery reg" 3Fh of the CRTC
        mov     dl, CRTC_ADDR           ; 
        out     dx, ax                  ;write a 1 to the color location
        mov     dl, CRTC_ADDR-20h       ; 
        out     dx, ax                  ;and to the mono location as well

;/*
;** END FIX for timing problem on IBM VGA
;*/
@@:
        mov     dl, GRAF_ADDR
        mov     al, GRAF_MISC
        mov     ah, [ecx].vga_grf_misc
        out     dx, ax                  ;set GRAF_MISC to required configuration

        mov     dl, SEQ_ADDR
        mov     ax, 0300h               ;turn synchronous reset bit back on
        out     dx, ax

res_hw_exit:
        mov     al, [ecx].vga_seq_indx
        mov     dl, SEQ_ADDR
        out     dx, al

        mov     al, [ecx].vga_grf_indx             ;             55847
        mov     dl, GRAF_ADDR                      ;             55847
        out     dx, al                             ;             55847

        ret
res_hw_regs ENDP



;/***************************************************************************
;*
;* FUNCTION NAME = GetEIPinEAX
;*
;* DESCRIPTION   = Copies the value pointed to by ESP into EAX
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
ALIGN 4
GetEIPinEAX PROC SYSCALL
        MOV     EAX,DWORD PTR[ESP]
        RET
GetEIPinEAX ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = GetEIPinEBX
;*
;* DESCRIPTION   = Copies the value pointed to by ESP into EBX
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
ALIGN 4
GetEIPinEBX PROC SYSCALL
        MOV     EBX,DWORD PTR[ESP]
        RET
GetEIPinEBX ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = GetEIPinEDX
;*
;* DESCRIPTION   = Copies the value pointed to by ESP into EDX
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
ALIGN 4
GetEIPinEDX PROC SYSCALL
        MOV     EDX,DWORD PTR[ESP]
        RET
GetEIPinEDX ENDP
endif ;768

_PtrCode32 ENDS
        END
