;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = POINTER.ASM
;*
;* DESCRIPTIVE NAME = Pointer shape routines
;*
;*
;* VERSION      V2.0
;*
;* DATE         02/23/87
;*
;* 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 poin    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.                                                    
;*              
;*  These routines must use the DS passed into the routines.  It
;*  is possible that we could be called out-of-context to draw
;*  the pointer.  If DataBASE, CodeData, or whatever is used, it
;*  might not be a valid selector for the current process.
;*
;* FUNCTIONS    move_pointers
;*              create_masks_1_thru_7
;*              pointer_off
;*              draw_pointer
;*              compute_rects
;*              clip_rects
;*              do_clipping
;*              xor_to_screen
;*              color_pointer_to_screen
;*              cps_do_a_pass
;*              and_into_work
;*              and_from_screen
;*              and_from_save
;*              copy_things_around
;*              copy_save_to_screen
;*              copy_screen_to_save
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/28/88                     Walt Moore [waltm] Complete rewrite.
;*                                Added X,Y as parameters
;*   01/16/89                     Walt Moore [waltm] PTR HM00371.  Didn't
;*                                handle a mask being all FFs or 00s.
;*   02/08/89                     Hock Lee [hockl] Added color pointer
;*                                (DCR 24327).
;*
;*****************************************************************************/

        .286p

        .xlist
        include cmacros.inc
        include pmgre.inc
        include driver.inc
        include pointer.inc
        include egamemf.inc
        include egafam.inc
        include assert.mac
        include njmp.mac
        .list

        .286p

        ??_out  pointer

        externA SCREEN_CBSCAN           ;Width of a scanline
        externA SCREEN_CY               ;Height of the screen
        externA SCREEN_CX               ;width of the screen in pels


;/*
;** POINTB is used for points with a range of -128..+127.
;*/

POINTB          struc
ptb_x           db      0
ptb_y           db      0
POINTB          ends


;/*
;** SIZEB is used for extents with a range of -128..+127.
;*/

SIZEB           struc
sizb_cx         db      0
sizb_cy         db      0
SIZEB           ends


;/*
;** SIZES is used for extents with a range of +/- 32K
;*/

SIZES           struc
sizs_cx         dw      0
sizs_cy         dw      0
SIZES           ends


;/*
;** The RECT_DATA structure is used for describing the rectangles
;** which will be manipulated by this code.  The fields are:
;**
;** rd_ptbSave    This is the (X,Y) origin of the given rectangle in
;**               the save area.
;**
;** rd_ptbScreen  This is the (X,Y) origin of the given rectangle on
;**               the screen.
;**
;** rd_sizb       This is the extents of the rectangle.
;**
;** rd_ptbWork    This is the (X,Y) origin of the given rectangle in
;**               the work area.
;*/

RECT_DATA       struc
rd_ptbSave      dw      ((size POINTB)/2) dup (0)
rd_ptsScreen    dw      ((size POINTS)/2) dup (0)
rd_sizb         dw      ((size SIZEB)/2)  dup (0)
rd_ptbWork      dw      ((size POINTB)/2) dup (0)
RECT_DATA       ends
                .errnz  size POINTB and 1
                .errnz  size POINTS and 1
                .errnz  size SIZEB  and 1
                .errnz  size SIZES  and 1

;/*
;** The POINTER_DATA structure is used for describing the actual pointer's
;** rectangle.  It also contains clipping information and control flags.
;** The fields are:
;**
;** pd_rd         RECT_DATA structure as defined above
;**
;** pd_fb         Flags as follows:
;**
;** PD_VALID        1 The rectangle contains valid data.
;**                 0 The rectangle data is invalid.
;**
;** PD_CLIP_BOTTOM  1 Clip the bottom
;**                 0 No bottom clipping needed
;**
;** PD_CLIP_TOP     1 Clip the top
;**                 0 No top clipping needed
;**
;** PD_CLIP_LEFT    1 Clip the lhs
;**                 0 No lhs clipping needed
;**
;** PD_CLIP_RIGHT   1 Clip the rhs
;**                 0 No rhs clipping needed
;*/

POINTER_DATA    struc
pd_rd           dw      ((size RECT_DATA)/2) dup (0)
pd_fb           db      0
                db      0
POINTER_DATA    ends
                .errnz  size RECT_DATA and 1
                .errnz  size RECTS and 1

PD_CLIP_BOTTOM  equ     10000000b
PD_CLIP_TOP     equ     01000000b
PD_CLIP_RIGHT   equ     00100000b
PD_CLIP_LEFT    equ     00010000b
PD_VALID        equ     00001000b

PD_CLIPPED      equ     PD_CLIP_BOTTOM or PD_CLIP_TOP or PD_CLIP_RIGHT or PD_CLIP_LEFT


sBegin  PtrData

        public  pdPtr1
        public  pdPtr2
        public  rdFlushX
        public  rdFlushY
        public  rdOverlap
        public  rdReadX
        public  rdReadY
        public  rdWork


pdPtr1          POINTER_DATA <>         ;Old/New pointer's data
pdPtr2          POINTER_DATA <>         ;Old/New pointer's data
rdFlushX        RECT_DATA <>            ;Flush from save area to screen
rdFlushY        RECT_DATA <>            ;Flush from save area to screen
rdOverlap       RECT_DATA <>            ;And from save area to work area
rdReadX         RECT_DATA <>            ;Read from screen to save, xor to work
rdReadY         RECT_DATA <>            ;Read from screen to save, xor to work
rdWork          RECT_DATA <>            ;Xor from work to screen


;/*
;**  This is a copy of the screen selector which we can use at interrupt
;**  time.  It is initialized in one_time_init in INIT.ASM.
;*/

        public  selScreenPtr
selScreenPtr    dw      -1


;/*
;**  siz?Mask contains the width and height of the working portion of
;**  the current AND and XOR mask.  Use of this allows us to manipulate
;**  less memory when parts of the pointer won't alter the screen image.
;*/

sizbMask        SIZEB   <WORK_WIDTH,PTR_HEIGHT>
sizsMask        SIZES   <WORK_WIDTH,PTR_HEIGHT>


;/*
;**  sizsMaxDelta is the maximum distance the old and new pointers may
;**  be before they are considered disjoint.
;*/

sizsMaxDelta    SIZES   <WORK_WIDTH,WORK_HEIGHT>


;/*
;**  ptsBotRightClip is the coordinate where rhs or bottom clipping
;**  will first occur.  It is basically the screen width - pointer width.
;*/

ptsBotRightClip POINTS  <SCREEN_CBSCAN-WORK_WIDTH,SCREEN_CY-WORK_HEIGHT>


;/*
;** This is the initial origin in the save buffer.
;*/

ptbInitOrigin   POINTB  <0,0>


;/*
;** nppdOld is the pointer to the old pointer's POINTER_DATA structure
;*/

nppdOld         dw      PtrDataOFFSET pdPtr1


;/*
;** nppdNew is the pointer to the new pointer's POINTER_DATA structure
;*/

nppdNew         dw      PtrDataOFFSET pdPtr2


;/*
;**  npAndXor is the pointer to which AND/XOR mask is to be used.  It is
;**  based on the 3 least significant bits of the poinetr's X coordinate.
;**  npColor is the pointer to which COLOR mask is to be used.
;*/

npAndXor        dw      INVALID_ADDRESS
npColor         dw      INVALID_ADDRESS


;/*
;**  The following are the masks which make up the pointer image.  There
;**  will be one AND/XOR/COLOR mask pair for each possible alignment.  On
;**  move_pointers call, all the alignments will be generated to save time
;**  processing the interrupt.
;*/

base_and_masks  equ     this byte
        db      (MASK_LENGTH * 8) dup (?)

base_xor_masks  equ     this byte
        db      (MASK_LENGTH * 8) dup (?)

base_clr_masks  equ     this byte
        db      (CLR_MASK_LENGTH * 8) dup (?)

;/*
;** npabAndMasks is an array which points to the start of the mask for
;** each X rotation.  It is indexed into using the low 3 bits of the
;** pointer's X coordinate.
;*/

npabAndMasks    equ     this word
        dw      base_and_masks+(0*MASK_LENGTH)
        dw      base_and_masks+(1*MASK_LENGTH)
        dw      base_and_masks+(2*MASK_LENGTH)
        dw      base_and_masks+(3*MASK_LENGTH)
        dw      base_and_masks+(4*MASK_LENGTH)
        dw      base_and_masks+(5*MASK_LENGTH)
        dw      base_and_masks+(6*MASK_LENGTH)
        dw      base_and_masks+(7*MASK_LENGTH)

;/*
;** npabClrMasks is an array which points to the start of the mask for
;** each X rotation.  It is indexed into using the low 3 bits of the
;** pointer's X coordinate.
;*/

npabClrMasks    equ     this word
        dw      base_clr_masks+(0*CLR_MASK_LENGTH)
        dw      base_clr_masks+(1*CLR_MASK_LENGTH)
        dw      base_clr_masks+(2*CLR_MASK_LENGTH)
        dw      base_clr_masks+(3*CLR_MASK_LENGTH)
        dw      base_clr_masks+(4*CLR_MASK_LENGTH)
        dw      base_clr_masks+(5*CLR_MASK_LENGTH)
        dw      base_clr_masks+(6*CLR_MASK_LENGTH)
        dw      base_clr_masks+(7*CLR_MASK_LENGTH)

;/*
;**  The following flags and flag bytes are used to control which
;**  rectangles are used for what.
;** 
;**  fbFlush controls which rectangles are to be copied from the save
;**  area to the screen.  Valid flags are:
;** 
;**    FB_OLD_PTR, FB_FLUSH_X, FB_FLUSH_Y
;** 
;**    FB_OLD_PTR is mutually exclusive of all other flags
;** 
;** 
;**  fbAndRead controls which rectangles are to be ANDed into the work
;**  area from the screen or save area, and which rectangles are to be
;**  copied from the screen to the save area.  Valid flags are:
;** 
;**    FB_NEW_PTR, FB_OVERLAP, FB_READ_X, FB_READ_Y, FB_WORK_RECT,
;** 
;**    FB_NEW_PTR and FB_WORK_RECT are mutually exclusive of all other
;**    flags.  Note that FB_OVERLAP doesn't apply when coping into the
;**    save area.
;** 
;** 
;**  fbXor describes which rectangle is to be XORed from the work area
;**  into the screen.  Valid flags are:
;** 
;**    FB_NEW_PTR, FB_WORK_RECT
;** 
;**    FB_NEW_PTR and FB_WORK_RECT are mutually exclusive
;*/

fbFlush         db      0
fbAndRead       db      0
fbXor           db      0
FB_OLD_PTR      equ     10000000b
FB_NEW_PTR      equ     01000000b
FB_FLUSH_X      equ     00100000b
FB_FLUSH_Y      equ     00010000b
FB_OVERLAP      equ     00001000b
FB_READ_X       equ     00000100b
FB_READ_Y       equ     00000010b
FB_WORK_RECT    equ     00000001b

;/*
;** fbPointer tells us if the pointer is color
;*/

        public  fbPointer
fbPointer       db      0
FBPTR_COLOR     equ     00000001b

sEnd    PtrData

sBegin  PtrCode
        assumes cs,PtrCode

        externW  PtrCodeData
        externNP save_hw_regs
        externNP res_hw_regs
page

;/***************************************************************************
;*
;* FUNCTION NAME = move_pointers
;*
;* DESCRIPTION   = 
;*
;*  The AND, XOR and COLOR pointer masks are stored in the pointer work
;*  areas.  The original mask will be pre-rotated for all eight
;*  possible alignments within a byte.
;*
;*  As the pointer is copied, it will be processed to see if it
;*  can be made narrower.  After it has been copied, it will be
;*  processed to see if it can be made shorter.
;*
;*  The following table indicates how the XOR/AND bitmap interacts with
;*  the COLOR bitmap for a color system:
;*
;*      XOR     AND     COLOR   Result
;*      1       1       x       invert screen
;*      0       0       x       use x
;*      0       1       x       transparent
;*      1       0       x       use x
;*
;*  From the table, we observe that when the AND bits are on, the
;*  corresponding COLOR bits are irrelevant.  We preprocess the
;*  COLOR bitmap to mask off these bits.
;*
;*  When drawing the color pointer, we do the following steps:
;*      1. XOR the destination with XOR mask.
;*      2. AND the destination with AND mask.  Note the zero bits
;*         would mask off the destination to prepare for the COLOR mask.
;*      3. OR the destination with the COLOR mask.  Note it does not
;*         affect the inverted or transparent bits since we have masked
;*         off the corresponding COLOR bits during preprocessing.
;*
;*                 Registers Preserved:
;*                       BP
;*                 Registers Destroyed:
;*                       AX,BX,CX,DX,SI,DI,DS,ES,FLAGS
;*                 Calls:
;*                       create_masks_1_thru_7
;*
;* INPUT         = NONE
;* OUTPUT        = AX = width in pels for exclusion hit test
;*                 DX = height in scans for exclusion hit test
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

PTRI_ANIMATE equ 2      

        assumes ds,nothing
        assumes es,nothing

cProc   move_pointers,<NEAR,PUBLIC,NO_ATTRPROF>
        parmD   pBitsAndXor             ;--> AND/XOR bitmap bitmask
        parmD   pBitsColor              ;--> color bitmap bitmask
        parmW   wFlags                  ;pointer type
cBegin
        mov     es,PtrCodeData
        assumes es,PtrData
        cld

;/*
;** The image we are copying is PTR_WIDTH bytes wide.  We must add an
;** extra byte to make it WORK_WIDTH wide.  The byte we add will depends
;** on whether this is the AND or the XOR mask.  For an AND mask, we add
;** an FF byte on the end of each scan.  For an XOR mask, we add a 00
;** byte on the end of each scan.  For the COLOR mask, we add a 00 byte on
;** the end of each scan of all planes.  These bytes won't alter anything
;** on the screen.
;*/

;/*
;** Copy the AND mask over.  As we copy it, accumulate the value of
;** each column of the mask.  If the entire column is FF, we may be
;** able to discard it.
;*/

        mov     fbPointer,0             ;Assume it's a b/w pointer
        mov     di,PtrDataOFFSET base_and_masks
        mov     cx,PTR_HEIGHT           ;Set height for move
        CPUMode 386
        mov     ebx,0FFFFFFFFh          ;Accumulate mask columns
        lds     si,pBitsAndXor          ;DS:SI --> AND/XOR mask
        assumes ds,nothing

move_next_and_mask_scan:
        lodsd                           ;Move explicit part
        stosd
        and     ebx,eax
        mov     al,0FFh
        stosb
        .errnz  PTR_WIDTH-4
        .errnz  WORK_WIDTH-5
        loop    move_next_and_mask_scan
        push    ebx                     ;Save AND column mask
        mov     dx,PtrDataOFFSET base_and_masks
        mov     cx,(MASK_LENGTH*7)/(WORK_WIDTH*2)
        .errnz  (MASK_LENGTH*7) mod (WORK_WIDTH*2)
        call    create_masks_1_thru_7

;/*
;** Copy the XOR mask over.  As we copy it, accumulate the value of
;** each column of the mask.  If the entire column is 00, we may be
;** able to discard it.
;*/

        assert  di,E,<PtrDataOFFSET base_xor_masks>
        mov     cx,PTR_HEIGHT           ;Set height for move
        xor     ebx,ebx                 ;Accumulate columns of the mask

move_next_xor_mask_scan:
        lodsd                           ;Move explicit part
        stosd
        or      ebx,eax
        xor     al,al
        stosb
        .errnz  PTR_WIDTH-4
        .errnz  WORK_WIDTH-5
        loop    move_next_xor_mask_scan
        mov     dx,PtrDataOFFSET base_xor_masks
        mov     cx,(MASK_LENGTH*7)/(WORK_WIDTH*2)
        .errnz  (MASK_LENGTH*7) mod (WORK_WIDTH*2)
        call    create_masks_1_thru_7

;/*
;**  The masks have been copied.  Compute the number of columns which can
;**  be discarded.  To discard a column, all bits of the AND mask for that
;**  column must be 1, and all bits of the XOR mask for the column must be
;**  0.  Since we work with bytes in this code, this must be true for an
;**  entire byte.
;**
;** Also note that the columns must be processed right to left.  We cannot
;** throw out a middle column if its neighbors contain data.
;*/

        pop     eax                     ;EAX AND mask
                                        ;EBX XOR mask
        not     eax
        or      eax,ebx                 ;Discard only if both are zero!

        mov     bx,WORK_WIDTH
        mov     dx,PTR_HEIGHT           ;assume full mask
        xor     cx,cx

        test    wFlags,PTRI_ANIMATE     ;force full mask if doing animation
        jnz     mp_have_sizes

        mov     dx,ax                   ;DX = bytes 1 and 2
        shr     eax,16                  ;AX = bytes 3 and 4
        or      ah,ah                   ;Discard 4th byte of mask?
        jnz     @F                      ;  No
        dec     bx
        or      al,al
        jnz     @F
        dec     bx
        CPUMode 286
        or      dh,dh
        jnz     @F
        dec     bx
        or      dl,dl
        njz     move_pointers_done      ;AX = DX = 0 for return codes
@@:

;/*
;** Compute the number of rows which can be discarded off the bottom.
;** To discard a row, all bits of the AND mask for that row must be a
;** 1, and all bits of the XOR mask for that row must be 0.
;*/

        .errnz  PTR_WIDTH and 1         ;Must be a word multiple
        push    ds
        pop     es
        assumes es,nothing
        dec     si                      ;Post decremnent, not pre decrement
        dec     si
        lea     di,[si][-PTR_WIDTH*PTR_HEIGHT] ;Last word of AND mask
        mov     cx,(PTR_WIDTH/2)*PTR_HEIGHT
        mov     ax,0FFFFh               ;Processing AND mask
        std
        repe    scasw
        mov     dx,cx                   ;Save count
        mov     di,si                   ;--> XOR mask
        mov     cx,(PTR_WIDTH/2)*PTR_HEIGHT
        xor     ax,ax                   ;Processing XOR mask
        repe    scasw
        cld                             ;Take care of this while we remember
        cmp     cx,dx                   ;Want |cx| to be the largest
        ja      @F
        xchg    cx,dx
@@:
;/*
;**  CX   >> 1   +1
;**
;**  63    31    32    1st word did not match, don't chop any scans
;**  62    31    32    2nd word did not match, don't chop any scans
;**  61    30    31    3rd word did not match, chop 1 scan
;**  60    30    31    4th word did not match, chop 1 scan
;*/

        .errnz  PTR_WIDTH-4
        shr     cx,1
        inc     cx                      ;CX = working height
        mov     dx,PTR_HEIGHT
        sub     dx,cx                   ;DX = # scans chopped off bottom
        xchg    cx,dx                   ;Height in DX for returning

;/*
;** BX = working width of the pointer image in bytes.  CX = amount to
;** chop of the bottom of the pointer image.  DX = working height of
;** the pointer image.
;*/

mp_have_sizes:
        mov     ds,PtrCodeData
        assumes ds,PtrData

        mov     ax,bx
        mov     sizsMask.sizs_cx,ax
        mov     sizsMaxDelta.sizs_cx,ax
        mov     ah,dl
        mov     sizbMask,ax
        .errnz  sizb_cy-sizb_cx-1
        mov     ax,dx
        mov     sizsMask.sizs_cy,ax
        mov     sizsMaxDelta.sizs_cy,ax
        neg     ax
        add     ax,SCREEN_CY
        mov     ptsBotRightClip.pts_y,ax
        mov     ax,SCREEN_CBSCAN
        sub     ax,bx
        mov     ptsBotRightClip.pts_x,ax
        shr     cx,1
        mov     ax,WORK_WIDTH
        sub     ax,bx
        shr     ax,1
        mov     ah,cl
        mov     ptbInitOrigin,ax
        .errnz  ptb_y-ptb_x-1
        mov     ax,bx
        dec     ax
        shl     ax,3                    ;Bit count is needed

        push    dx                      ;Remember return values
        push    ax

;/*
;** Finally, copy the COLOR mask over.  As we copy it, mask off the
;** corresponding AND bits in the COLOR mask since we do not use that
;** color bit if the AND bit is on.
;**
;**       XOR     AND     COLOR
;**       1       1       invert screen
;**       0       0       use color
;**       0       1       transparent
;**       1       0       use color
;*/

        mov     cx,pBitsColor.sel       ;Set DS:SI --> color bitmap bits
        jcxz    move_color_pointer_done ;pBitsColor.sel was set to zero
        mov     ds,cx                   ;  if a null bitmap was passed in
        assumes ds,nothing
        mov     si,pBitsColor.off
        mov     es,PtrCodeData
        assumes es,PtrData
        mov     di,PtrDataOFFSET base_clr_masks
        push    bp                      ;Need extra loop counter
        mov     bp,BITS_PEL

move_next_color_mask_plane:
        mov     cx,PTR_HEIGHT           ;Set height for move
        mov     bx,PtrDataOFFSET base_and_masks
        sub     bx,di                   ;make it relative

move_next_color_mask_scan:

ifdef   WITH_AND_MASK
        lodsw                           ;Copy a scan from the current plane
        mov     dx,es:[bx][di]          ;Mask off the corresponding AND bits
        not     dx
        and     ax,dx
        stosw
        lodsw
        mov     dx,es:[bx][di]
        not     dx
        and     ax,dx
        stosw
else
        movsw
        movsw
endif;  WITH_AND_MASK

        xor     al,al
        stosb
        .errnz  PTR_WIDTH-4
        .errnz  WORK_WIDTH-5
        add     si,(BITS_PEL-1)*PTR_WIDTH
        loop    move_next_color_mask_scan
        sub     si,(BITS_PEL*PTR_WIDTH*PTR_HEIGHT)-PTR_WIDTH
        dec     bp
        jnz     move_next_color_mask_plane
        pop     bp                      ;Restore register

        mov     dx,PtrDataOFFSET base_clr_masks
        mov     cx,(CLR_MASK_LENGTH*7)/(WORK_WIDTH*2)
        .errnz  (CLR_MASK_LENGTH*7) mod (WORK_WIDTH*2)
        call    create_masks_1_thru_7
        mov     fbPointer,FBPTR_COLOR   ;Indicate color pointer

move_color_pointer_done:

        pop     ax                      ;return results in DX,AX
        pop     dx

move_pointers_done:
        fw_zero <es,ds>

cEnd
page

;/***************************************************************************
;*
;* FUNCTION NAME = create_masks_1_thru_7
;*
;* DESCRIPTION   = 
;*
;*    The pointer shape has been copied into our memory.  Now pre-rotate
;*    the pointer for all the different alignments.  Simply put:
;*
;*                    pointer image              fill byte
;*
;*          |ABCDEFGH|IJKLMNOP|QRSTUVWX|YZabcdef|00000000|
;*
;*     becomes this for (x mod 8) = 1
;*
;*          |0ABCDEFG|HIJKLMNO|PQRSTUVW|XYZabcde|f0000000|
;*
;*     and this for (x mod 8) = 2
;*
;*          |00ABCDEF|GHIJKLMN|OPQRSTUV|WXYZabcd|ef000000|
;*
;*
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,CX,DX,DI
;*
;* INPUT         = ES:DI --> first byte of mask for phase alignment 1
;*                 ES:DX --> first byte of mask for phase alignment 0
;*                 CX     =  (mask length * 7) / (WORK_WIDTH * 2)
;*                 AL     =  fill value (00 or FF)
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing              ;DS is pointing to bitmap
        assumes es,nothing

cProc   create_masks_1_thru_7,<NEAR,PUBLIC,NO_ATTRPROF>
cBegin

;/*
;** Since the masks are contiguous, we can do it as one very long loop,
;** where the results of rotating the previous mask by one becomes the
;** source for the next rotate by one.
;*/

        xchg    si,dx
        add     al,al                   ;Set initial 'C' value

rotate_next_two_scans:
        lods    word ptr es:[si]
        rcr     al,1
        rcr     ah,1
        stosw

        lods    word ptr es:[si]
        rcr     al,1
        rcr     ah,1
        stosw

        lods    word ptr es:[si]
        rcr     al,1
        rcr     ah,1
        stosw

        lods    word ptr es:[si]
        rcr     al,1
        rcr     ah,1
        stosw

        lods    word ptr es:[si]
        rcr     al,1
        rcr     ah,1
        stosw

        loop    rotate_next_two_scans
        .errnz  (WORK_WIDTH*2)-10

        xchg    si,dx
cEnd
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
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,nothing

cProc   pointer_off,<NEAR,PUBLIC,NO_ATTRPROF>
cBegin  <nogen>

        pop     bx
        push    SCREEN_CX               ;First location off the screen
        push    ax                      ;Only one has to be clipped!
        push    bx
        errn$   draw_pointer

cEnd    <nogen>
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    
;*                 Calls:
;*                       save_hw_regs
;*                       res_hw_regs
;*                       compute_rects
;*                       clip_rects
;*                       and_into_work
;*                       copy_things_around
;*                       xor_to_screen
;*                       color_pointer_to_screen
;*
;* INPUT         = DS = PtrData
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,nothing

cProc   draw_pointer,<NEAR,PUBLIC,NO_ATTRPROF>
        parmW   ptsX
        parmW   ptsY
cBegin
        push    bp                      ;DO NOT AUTOSAVE THIS!
        cld
        mov     di,nppdNew              ;--> new pointer's data goes here
        mov     si,nppdOld              ;--> old pointer's data
        mov     ax,ptsX
        mov     bx,ax
        and     bx,00000111b
        add     bx,bx
        mov     dx,npabAndMasks[bx]
        mov     npAndXor,dx
        mov     dx,npabClrMasks[bx]
        mov     npColor,dx
        mov     bx,PD_VALID             ;Assume visible
        sar     ax,3                    ;Compute starting byte address (set 'S')
        mov     [di].pd_rd.rd_ptsScreen.pts_x,ax

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

        js      clip_lhs_of_pointer     ;If X is negative, lhs clipping needed
        sub     ax,ptsBotRightClip.pts_x
        jle     done_x_clipping
        mov     bh,PD_CLIP_RIGHT        ;AX = amount to clip off rhs
        jmp     short finish_x_clip

clip_lhs_of_pointer:
        neg     ax                      ;Want |ax|
        mov     bh,PD_CLIP_LEFT

finish_x_clip:
        cmp     ax,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     ax,ptsY
        mov     [di].pd_rd.rd_ptsScreen.pts_y,ax
        or      ax,ax
        js      clip_top_of_pointer     ;If Y is negative, top clipping needed
        sub     ax,ptsBotRightClip.pts_y
        jle     done_y_clipping
        mov     bh,PD_CLIP_BOTTOM       ;AX = amount to clip off bottom
        jmp     short 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    [si].pd_fb,PD_VALID
        jz      draw_pointer_exit       ;No new, no old
        xor     ax,ax
        mov     [di].pd_fb,al           ;Clear PD_VALID flag, clipping flags
        mov     word ptr fbAndRead,ax   ;Nothing to read/and/xor
        .errnz  fbXor-fbAndRead-1
        mov     fbFlush,FB_OLD_PTR      ;Write old to screen
        jmp     short rectangles_been_computed


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

clip_top_of_pointer:
        neg     ax                      ;Want |ax|
        mov     bh,PD_CLIP_TOP

finish_y_clip:
        cmp     ax,sizsMask.sizs_cy     ;Height of pointer in scans
        jge     not_visible             ;Clipped away too much
        or      bl,bh

done_y_clipping:
        mov     [di].pd_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,sizbMask             ;ptbSave will be set by compute_rects
        mov     [di].pd_rd.rd_sizb,ax
        .errnz  (size SIZEB) - 2
        xor     ax,ax
        mov     [di].pd_rd.rd_ptbWork,ax
        .errnz  (size POINTB) - 2

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

        call    compute_rects
rectangles_been_computed:

        call    clip_rects
        mov     es,selScreenPtr
        assumes es,EGAMem
        call    save_hw_regs            ;Save EGA registers

        test    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,fbAndRead
        or      al,al
        jz      done_and_portion
        call    and_into_work
done_and_portion:

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

        mov     ax,word ptr fbFlush     ;Assume nothing to copy to/from save
        .errnz  fbAndRead-fbFlush-1
        or      ah,al
        jz      copied_things_around
        call    copy_things_around
copied_things_around:


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

        mov     al,fbXor
        or      al,al
        jz      pointer_drawn
        call    xor_to_screen
        jmp     short pointer_drawn

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

public  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 fbFlush     ;Assume nothing to copy to/from save
        .errnz  fbAndRead-fbFlush-1
        or      ah,al
        jz      things_copied_around
        call    copy_things_around
things_copied_around:

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

        test    fbXor,0FFh
        jz      pointer_drawn
        call    color_pointer_to_screen ;Planes must all be enabled

pointer_drawn:
        call    res_hw_regs             ;Restore user's state
        mov     ax,nppdNew
        xchg    ax,nppdOld
        mov     nppdNew,ax
draw_pointer_exit:
        pop     bp                      ;DO NOT AUTOSAVE THIS

cEnd
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
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,PtrData

compute_rects   proc    near

        mov     word ptr fbFlush,ax     ;Assume nothing to restore to screen
        .errnz  fbAndRead-fbFlush-1     ;Assume nothing to read/And to work
        mov     fbXor,FB_NEW_PTR        ;Assume new pointer is XORed to screen
        test    [si].pd_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     ax,[di].pd_rd.rd_ptsScreen.pts_x
        sub     ax,[si].pd_rd.rd_ptsScreen.pts_x
        mov     bl,al                   ;BL = delta x
        abs_ax
        cmp     ax,sizsMaxDelta.sizs_cx
        jae     rects_are_disjoint
        mov     ax,[di].pd_rd.rd_ptsScreen.pts_y
        sub     ax,[si].pd_rd.rd_ptsScreen.pts_y
        mov     bh,al                   ;BH = delta y
        abs_ax
        cmp     ax,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     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:
        mov     ax,ptbInitOrigin        ;Reset origin within the save area
        mov     [di].pd_rd.rd_ptbSave,ax
        .errnz  (size POINTB) - 2
        mov     fbAndRead,FB_NEW_PTR    ;Copy new ptr to save and XOR to work
        jmp     compute_rects_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 pd_rd.rd_ptbSave
;**       BH     =  dy
;**       BL     =  dx (negative)
;**       DS:SI --> old pointer's rd_ptsScreen
;**       DS:DI --> 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.
;*/

        lodsw
        .errnz  pd_rd.rd_ptbSave
        mov     rdOverlap.rd_ptbSave,ax
        mov     [di].pd_rd.rd_ptbSave,ax
        xchg    ax,dx

;/*
;** Set old pointer's screen (X,Y) into the overlap rectangle as the
;** screen origin.
;*/

        lodsw
        .errnz  rd_ptsScreen-rd_ptbSave-2
        .errnz  pts_x
        mov     rdOverlap.rd_ptsScreen.pts_x,ax
        xchg    ax,bp
        lodsw
        .errnz  pts_y-pts_x-2
        mov     rdOverlap.rd_ptsScreen.pts_y,ax
        xchg    ax,si

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

        mov     ax,sizbMask
        mov     rdOverlap.rd_sizb,ax
        .errnz  sizb_cy-sizb_cx-1
        .errnz  (size SIZEB) - 2
        xchg    ax,cx
        .errnz  sizb_cy-sizb_cx-1

;/*
;** Set the work buffer origin to be zero
;*/

        xor     ax,ax
        mov     rdOverlap.rd_ptbWork,ax

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

        mov     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
;**                          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     rdReadX.rd_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     rdOverlap.rd_sizb.sizb_cx,al

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

        add     ax,bp                   ;AX = sizbMask.sizb_cx - |dx|
        mov     rdFlushX.rd_ptsScreen.pts_x,ax

;/*
;** 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     ax,dx                   ;DX = old ptbSave
        add     al,bl                   ;BL = dx (negative)
        add     ah,bh                   ;BH = dy
        and     ax,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        mov     [di].pd_rd.rd_ptbSave.ptb_x,al
        mov     rdReadX.rd_ptbSave,ax
        .errnz  ptb_y-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     rdFlushX.rd_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     rdFlushX.rd_sizb,ax
        mov     rdReadX.rd_sizb,ax
        .errnz  sizb_cy-sizb_cx-1
        mov     rdOverlap.rd_ptbWork.ptb_x,al

;/*
;** The Read buffer's screen address is the ptsScreen stored in the new
;** pointer's RECT_DATA.
;*/

        mov     ax,[di].pd_rd.rd_ptsScreen.pts_x
        jmp     short finish_x_overlap



;/*
;**  The starting X of the new rectangle is somewhere in the middle
;**  of the old 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     rdOverlap.rd_ptsScreen.pts_x,ax

;/*
;** 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     rdFlushX.rd_sizb,ax
        mov     rdReadX.rd_sizb,ax
        .errnz  sizb_cy-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     [di].pd_rd.rd_ptbSave.ptb_x,al
        mov     rdOverlap.rd_ptbSave.ptb_x,al

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

        mov     ax,dx
        mov     rdFlushX.rd_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     ax,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        mov     rdReadX.rd_ptbSave,ax
        .errnz  ptb_y-ptb_x-1

;/*
;** The X screen origin of the flush buffer is the old ptsScreen.pts_x
;*/

        mov     rdFlushX.rd_ptsScreen.pts_x,bp

;/*
;** 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     rdOverlap.rd_sizb.sizb_cx,al
        cbw
        mov     rdReadX.rd_ptbWork,ax
        .errnz  ptb_y-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 rectangles
;** X coordinate + the save width
;*/

        mov     al,cl
        add     ax,bp

finish_x_overlap:
        mov     rdReadX.rd_ptsScreen.pts_x,ax
        mov     ax,[di].pd_rd.rd_ptsScreen.pts_y
        mov     rdReadX.rd_ptsScreen.pts_y,ax

;/*
;** The Y address of the flush rectangle on the screen is ptsScreen.pts_y.
;*/

        mov     rdFlushX.rd_ptsScreen.pts_y,si

;/*
;** 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 fbFlush,(FB_READ_X shl 8) + FB_FLUSH_X
        .errnz  fbAndRead-fbFlush-1
        xor     ax,ax

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    |    |
;**       * onononononononononononono    ---
;**                                  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     rdReadY.rd_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     rdOverlap.rd_sizb.sizb_cy,al

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

        add     ax,si                   ;AX = sizbMask.sizb_cy - |dy|
        mov     rdFlushY.rd_ptsScreen.pts_y,ax

;/*
;** 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     ax,dx                   ;DX = old ptbSave
        add     ah,bh                   ;BH = dy (negative)
        add     al,bl
        and     ax,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        mov     [di].pd_rd.rd_ptbSave.ptb_y,ah
        mov     rdReadY.rd_ptbSave,ax
        .errnz  ptb_y-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     rdFlushY.rd_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     rdFlushY.rd_sizb,ax
        mov     rdReadY.rd_sizb,ax
        .errnz  sizb_cy-sizb_cx-1
        mov     rdOverlap.rd_ptbWork.ptb_y,ah

;/*
;** The Read buffer's screen address is the ptsScreen stored in the new
;** pointer's RECT_DATA.
;*/

        mov     ax,[di].pd_rd.rd_ptsScreen.pts_y
        jmp     short 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    |    |
;**       * onononononononononononono    ---
;**                                  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     rdOverlap.rd_ptsScreen.pts_y,ax

;/*
;** 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     [di].pd_rd.rd_ptbSave.ptb_y,al
        mov     rdOverlap.rd_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     rdFlushY.rd_sizb,ax
        mov     rdReadY.rd_sizb,ax
        .errnz  sizb_cy-sizb_cx-1

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

        mov     ax,dx
        mov     rdFlushY.rd_ptbSave,dx

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

        add     ah,ch
        add     al,bl
        and     ax,((SAVE_BUFFER_HEIGHT-1) shl 8) + SAVE_BUFFER_WIDTH-1
        mov     rdReadY.rd_ptbSave,ax
        .errnz  ptb_y-ptb_x-1

;/*
;** The Y screen origin of the flush buffer is the old ptsScreen.pts_y
;*/

        mov     rdFlushY.rd_ptsScreen.pts_y,si

;/*
;** 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     rdOverlap.rd_sizb.sizb_cy,ah
        xor     al,al
        mov     rdReadY.rd_ptbWork,ax
        .errnz  ptb_y-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
;*/
        mov     al,ch
        xor     ah,ah
        add     ax,si

finish_y_overlap:
        mov     rdReadY.rd_ptsScreen.pts_y,ax
        mov     ax,[di].pd_rd.rd_ptsScreen.pts_x
        mov     rdReadY.rd_ptsScreen.pts_x,ax

;/*
;** The X address of the flush rectangle on the screen is ptsScreen.pts_x.
;*/

        mov     rdFlushY.rd_ptsScreen.pts_x,bp

;/*
;** 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 fbFlush,((FB_READ_Y or FB_OVERLAP) shl 8) + FB_FLUSH_Y
        .errnz  fbAndRead-fbFlush-1


;/*
;**  We have computed the seperate 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
        mov     bx,PtrDataOFFSET rdFlushX  ;Assume cases 1 and 2
        or      al,al
        jns     @F
        mov     bx,PtrDataOFFSET rdReadX   ;Its cases 3 and 4
        neg     al                      ;|dy|
        add     [bx].rd_ptbWork.ptb_y,al;Move down in the work area too!
@@:
        mov     cl,[bx].rd_ptbSave.ptb_y
        add     cl,al
        and     cl,SAVE_BUFFER_HEIGHT-1
        mov     [bx].rd_ptbSave.ptb_y,cl
        cbw                             ;Set AH = 0
        .errnz  (SAVE_BUFFER_HEIGHT-1) and 80h
        add     [bx].rd_ptsScreen.pts_y,ax
        neg     al
        add     al,ch                   ;sizbMask.sizb_cy - |dy|
        mov     rdFlushX.rd_sizb.sizb_cy,al
        mov     rdReadX.rd_sizb.sizb_cy,al

processed_y_overlap:
compute_rects_exit:
        ret

compute_rects   endp
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
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,PtrData

clip_rects      proc    near

        mov     si,nppdOld              ;--> old POINTER_DATA structure
        mov     dl,[si].pd_fb
        test    dl,PD_CLIPPED           ;Won't be set if PD_VALID isn't set
        jz      old_been_clipped

;/*
;**  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 it's contents.
;*/

        mov     di,si                   ;DI --> rectangle to clip
        mov     bl,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
        mov     di,PtrDataOFFSET rdFlushX
        mov     bh,FB_FLUSH_X
        test    bl,bh
        jz      @F
        call    do_clipping
@@:
        mov     di,PtrDataOFFSET rdFlushY
        mov     bh,FB_FLUSH_Y
        test    bl,bh
        jz      @F
call_do_clip:
        call    do_clipping
@@:
        mov     fbFlush,bl

        mov     di,PtrDataOFFSET rdOverlap
        mov     bl,fbAndRead
        mov     bh,FB_OVERLAP
        test    bl,bh
        jz      @F
        call    do_clipping
        mov     fbAndRead,bl
@@:
old_been_clipped:


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

        mov     si,nppdNew              ;--> new POINTER_DATA structure
        mov     dl,[si].pd_fb
        test    dl,PD_CLIPPED           ;Won't be set if PD_VALID isn't set
        jz      new_been_clipped

;/*
;** 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     rdWork.rd_ptbSave,ax
        .errnz  rd_ptbSave
        .errnz  (size POINTB) - 2

        lodsw
        mov     rdWork.rd_ptsScreen.pts_x,ax
        .errnz  rd_ptsScreen-rd_ptbSave-2
        .errnz  pts_x

        lodsw
        mov     rdWork.rd_ptsScreen.pts_y,ax
        .errnz  pts_y-pts_x-2

        lodsw
        mov     rdWork.rd_sizb,ax
        .errnz  (size SIZEB) - 2

        lodsw
        mov     rdWork.rd_ptbWork,ax
        .errnz  (size POINTB) - 2

        sub     si,size RECT_DATA       ;--> to start of POINTER_DATA
        .errnz  (rd_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.
;*/

        mov     di,PtrDataOFFSET rdWork
        call    do_clipping
        mov     bl,FB_WORK_RECT
        mov     fbXor,bl
        xchg    fbAndRead,bl            ;Assume only work rect to and/read
        test    bl,FB_NEW_PTR
        jnz     new_been_clipped

        mov     di,PtrDataOFFSET rdReadX
        mov     bh,FB_READ_X
        test    bl,bh
        jz      @F
        call    do_clipping
@@:
        mov     di,PtrDataOFFSET rdReadY
        mov     bh,FB_READ_Y
        test    bl,bh
        jz      @F
        call    do_clipping
@@:
        mov     fbAndRead,bl

new_been_clipped:
        ret

clip_rects      endp
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   =  pd_fb for [SI]
;*                 SI  --> POINTER_DATA structure
;*                 DI  --> RECT_DATA structure
;*
;* OUTPUT        = BL updated  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,PtrData

do_clipping     proc    near

        .errnz  pd_rd                   ;Must be at offset 0

        xor     ax,ax
        test    dl,PD_CLIP_BOTTOM or PD_CLIP_TOP
        jz      y_clipping_done
        mov     cx,[di].rd_ptsScreen.pts_y
        js      clip_on_bottom_eh?
        .errnz  PD_CLIP_BOTTOM-10000000b

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

clip_on_top:
        neg     cx                      ;If it was negative, then must clip
        jle     y_clipping_done         ;Was positive, no clipping needed
        sub     [di].rd_sizb.sizb_cy,cl ;Compute new height
        jle     clear_visible_bit       ;Clipped away, nothing visible
        add     [di].rd_ptbWork.ptb_y,cl;Move down in work area
        add     cl,[di].rd_ptbSave.ptb_y;Move down in save area
        and     cl,SAVE_BUFFER_HEIGHT-1
        mov     [di].rd_ptbSave.ptb_y,cl
        mov     [di].rd_ptsScreen.pts_y,ax
        jmp     short finish_y_clipping


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

clip_on_bottom_eh?:
        mov     al,[di].rd_sizb.sizb_cy
        add     cx,ax
        sub     cx,SCREEN_CY
        jle     finish_y_clipping       ;AX = amount to clip if positive
        sub     al,cl                   ;Compute new height
        jle     clear_visible_bit       ;Clipped away, nothing visible
        mov     [di].rd_sizb.sizb_cy,al
finish_y_clipping:
        xor     ax,ax
y_clipping_done:


        test    dl,PD_CLIP_LEFT or PD_CLIP_RIGHT
        jz      x_clipping_done
        mov     cx,[di].rd_ptsScreen.pts_x
        test    dl,PD_CLIP_RIGHT
        jnz     clip_on_rhs

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

clip_on_lhs:
        neg     cx                      ;If it was negative, then must clip
        jle     x_clipping_done         ;Was positive, no clipping needed
        sub     [di].rd_sizb.sizb_cx,cl ;Compute new width
        jle     clear_visible_bit       ;Clipped away, nothing visible
        add     [di].rd_ptbWork.ptb_x,cl;Move right in work area
        add     cl,[di].rd_ptbSave.ptb_x;Move right in save area
        and     cl,SAVE_BUFFER_WIDTH-1
        mov     [di].rd_ptbSave.ptb_x,cl
        mov     [di].rd_ptsScreen.pts_x,ax
        jmp     short x_clipping_done


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

clip_on_rhs:
        mov     al,[di].rd_sizb.sizb_cx
        add     cx,ax
        sub     cx,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     [di].rd_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

do_clipping     endp
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  
;*                 ES = EGAMem    
;*                 DS = PtrData
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

xor_to_screen   proc    near

        .errnz  pd_rd                   ;Must be at offset 0
        xor     bx,bx                   ;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     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax                   ;Enable all bits
        mov     ax,DR_XOR shl 8 + GRAF_DATA_ROT
        out16   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.
;*/

        mov     si,nppdNew              ;Assume new pointer is in use
        jnc     have_xor_mask_offset    ;BX = 0 is offset
        .errnz  FB_WORK_RECT-00000001b
        .erre   FB_NEW_PTR-00000001b
        mov     si,PtrDataOFFSET rdWork  ;The work area (we be clipping)
        mov     ax,[si].rd_ptbWork      ;Get origin in work area
        xchg    ah,bl                   ;BX = ptbWork.ptb_y, AX = ptbWork.ptb_x
        add     ax,bx                   ;*1 + X component
        add     bx,bx                   ;*2
        add     bx,bx                   ;*4
        add     bx,ax                   ;*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    di,[si].rd_ptsScreen.pts_y,SCREEN_CBSCAN
        add     di,[si].rd_ptsScreen.pts_x
        mov     cx,[si].rd_sizb
        .errnz  sizb_cy-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     si,npAndXor             ;Set address of XOR mask
        lea     si,[si][bx][base_xor_masks-base_and_masks]
        add     bx,offset abPointerWork
        sub     bx,si

;/*
;** 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     al,cl
        cbw
        mov     bp,WORK_WIDTH
        sub     bp,ax
        lea     dx,[bp][SCREEN_CBSCAN-WORK_WIDTH]
        add     ax,ax
        add     ax,ax
        neg     ax
        add     ax,PtrCodeOFFSET xor_to_screen_width_0
        jmp     ax
        .errnz  WORK_WIDTH-5
        errnz   xor_to_screen_width_0-xor_to_screen_width_1-4
        errnz   xor_to_screen_width_1-xor_to_screen_width_2-4
        errnz   xor_to_screen_width_2-xor_to_screen_width_3-4
        errnz   xor_to_screen_width_3-xor_to_screen_width_4-4
        errnz   xor_to_screen_width_4-xor_to_screen_width_5-4

;/*
;** Register usage for the loop will be:
;**
;**       AX    = loop starting address
;**       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
;*/

xor_to_screen_width_5:
        cmp     al,es:[bx][si]          ;Load latches from work area
        movsb                           ;XOR to the screen
xor_to_screen_width_4:
        cmp     al,es:[bx][si]
        movsb
xor_to_screen_width_3:
        cmp     al,es:[bx][si]
        movsb
xor_to_screen_width_2:
        cmp     al,es:[bx][si]
        movsb
xor_to_screen_width_1:
        cmp     al,es:[bx][si]
        movsb
xor_to_screen_width_0:
        add     si,bp                   ;--> next scan's XOR mask, work addr
        add     di,dx                   ;--> next scan's destination
        dec     ch
        jz      @F
        jmp     ax                      ;Next pass
@@:
        ret

xor_to_screen   endp

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         = ES = EGAMem
                   DS = PtrData
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

cProc   color_pointer_to_screen,<NEAR,PUBLIC,NO_ATTRPROF>
        localB  iPlaneMask
        localW  usdel_screen
        localW  usoffset_save
        localW  usoffset_mask
cBegin

        .errnz  pd_rd                   ;Must be at offset 0
        mov     iPlaneMask,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     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax                   ;Enable all bits
        mov     ax,DR_SET shl 8 + GRAF_DATA_ROT
        out16   dx,ax

cps_do_next_plane:

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

        mov     al,iPlaneMask           ;Set Map Mask
        mov     dx,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
        out16   dx,ax

        mov     si,nppdNew
        test    fbXor,FB_WORK_RECT
        jz      cps_have_pointer_rect
        mov     si,PtrDataOFFSET rdWork ;The work area (we be clipping)
cps_have_pointer_rect:

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

        imul    di,[si].rd_ptsScreen.pts_y,SCREEN_CBSCAN
        add     di,[si].rd_ptsScreen.pts_x
        mov     ax,[si].rd_ptbSave
        mov     dx,[si].rd_ptbWork
        .errnz  ptb_y-ptb_x-1
        mov     cx,[si].rd_sizb
        .errnz  sizb_cy-sizb_cx-1

        mov     bl,al                   ;See if save area is contiguous
        add     bl,cl
        sub     bl,SAVE_BUFFER_WIDTH    ;BL = is overhang
        jle     call_cps_do_a_pass      ;No overhang

        sub     cl,bl                   ;Set X extent for pass 1
        mov     bh,cl                   ;Need it for pass 2
        push    si                      ;Must keep rectangle pointer around
        push    di
        push    bx
        call    cps_do_a_pass           ;Process first half
        pop     bx
        pop     di
        pop     si
        mov     ax,[si].rd_ptbSave
        mov     dx,[si].rd_ptbWork
        .errnz  ptb_y-ptb_x-1
        mov     cx,[si].rd_sizb
        .errnz  sizb_cy-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     di,bx

call_cps_do_a_pass:
        call    cps_do_a_pass           ;Process second half
        shr     iPlaneMask,1
        jnc     cps_do_next_plane
        .errnz  MM_C0-00000001b
        .errnz  MM_C1-00000010b
        .errnz  MM_C2-00000100b
        .errnz  MM_C3-00001000b
cEnd

;/***************************************************************************
;*
;* 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         = BP = stack frame of color_pointer_to_screen                                
;*                 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                                                    
;*                 ES:SI --> Source        (save area)                                        
;*                 ES:DI --> Destination   (screen area)                                      
;*                 ES = EGAMem                                                                
;*                 DS = PtrData                                                               
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

        public  cps_do_a_pass
cps_do_a_pass   proc    near

;/*
;** 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
        xor     ah,ah
        mov     usoffset_save,ax
        .errnz  SAVE_BUFFER_WIDTH-8
        .errnz  SAVE_BUFFER_HEIGHT-32
        mov     si,offset abPointerSave
        add     si,ax                   ;SI --> start address of save area


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

        xor     bx,bx
        xchg    dh,bl
        add     dx,bx                   ;y * 1 + x
        add     bx,bx                   ;y * 2
        add     bx,bx                   ;y * 4
        add     bx,dx                   ;y * 5 + x
        mov     usoffset_mask,bx
        add     bx,npAndXor             ;BX --> start address of AND mask
        sub     bx,di                   ;Make BX relative to DI

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

        mov     al,cl
        cbw
        mov     dx,SCREEN_CBSCAN
        sub     dx,ax
        mov     usdel_screen,dx         ;Delta to next scan of screen area

;/*
;** Calculate DX = jump table address
;*/

        imul    ax,12                   ;12x
        mov     dx,PtrCodeOFFSET color_to_screen_width_0
        sub     dx,ax
        .errnz  WORK_WIDTH-5
        errnz   color_to_screen_width_0-color_to_screen_width_1-12
        errnz   color_to_screen_width_1-color_to_screen_width_2-12
        errnz   color_to_screen_width_2-color_to_screen_width_3-12
        errnz   color_to_screen_width_3-color_to_screen_width_4-12
        errnz   color_to_screen_width_4-color_to_screen_width_5-12

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

        push    bp                      ;Need an extra register
        mov     al,iPlaneMask
        mov     bp,usoffset_mask
        add     bp,npColor              ;BP --> start address of COLOR mask
        shr     al,1
        jz      @F
        add     bp,MASK_LENGTH
        shr     al,1
        jz      @F
        add     bp,MASK_LENGTH
        shr     al,1
        jz      @F
        add     bp,MASK_LENGTH
@@:
        sub     bp,di                   ;Make BP relative
        jmp     dx

;/*
;** Register usage for the loop will be:
;**
;**       DX    = loop starting address
;**       DS:BX = offset off [di] to the AND mask
;**       CL    =
;**       CH    = height
;**       DS:BP = offset off [di] to the COLOR mask
;**       ES:SI --> Source        (save area)
;**       ES:DI --> Destination   (screen area)
;*/

color_to_screen_width_5:
        lods    byte ptr es:[si]
        xor     al,[bx][di][base_xor_masks-base_and_masks]
        and     al,[bx][di]
        or      al,ds:[bp][di]
        stosb
color_to_screen_width_4:
        lods    byte ptr es:[si]
        xor     al,[bx][di][base_xor_masks-base_and_masks]
        and     al,[bx][di]
        or      al,ds:[bp][di]
        stosb
color_to_screen_width_3:
        lods    byte ptr es:[si]
        xor     al,[bx][di][base_xor_masks-base_and_masks]
        and     al,[bx][di]
        or      al,ds:[bp][di]
        stosb
color_to_screen_width_2:
        lods    byte ptr es:[si]
        xor     al,[bx][di][base_xor_masks-base_and_masks]
        and     al,[bx][di]
        or      al,ds:[bp][di]
        stosb
color_to_screen_width_1:
        lods    byte ptr es:[si]
        xor     al,[bx][di][base_xor_masks-base_and_masks]
        and     al,[bx][di]
        or      al,ds:[bp][di]
        stosb
color_to_screen_width_0:

        sub     bp,SCREEN_CBSCAN-WORK_WIDTH
        mov     ax,bp                   ;AX --> next scan's COLOR mask
        pop     bp

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

        xchg    ax,usoffset_save
        add     al,SAVE_BUFFER_WIDTH    ;Take into account wrap around
        mov     si,offset abPointerSave
        add     si,ax                   ;SI --> next scan's save area
        xchg    usoffset_save,ax

        add     di,usdel_screen         ;DI --> next scan's screen area
        sub     bx,SCREEN_CBSCAN-WORK_WIDTH
                                        ;BX --> next scan's AND mask
        dec     ch
        jz      @F
        push    bp
        mov     bp,ax                   ;BP --> next scan's COLOR mask
        jmp     dx                      ;Next pass
@@:
        ret

cps_do_a_pass   endp

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 
;*                 Calls:
;*                       and_from_screen
;*                       and_save_to_work
;*
;* INPUT         = AL = fbAndRead
;*                 ES = EGAMem
;*                 DS = PtrData
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

and_into_work   proc    near

;/*
;**  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.
;*/

        xchg    ax,cx
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,DR_AND shl 8 + GRAF_DATA_ROT
        out16   dx,ax
        xchg    ax,cx

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

        mov     si,nppdNew
        test    al,FB_NEW_PTR
        jnz     aiw_source_is_screen
        mov     si,PtrDataOFFSET 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
        mov     si,PtrDataOFFSET rdReadY
        call    and_from_screen
        mov     al,fbAndRead
@@:

        test    al,FB_READ_X
        jz      @F
        mov     si,PtrDataOFFSET rdReadX
aiw_source_is_screen:
        call    and_from_screen
        mov     al,fbAndRead
@@:

        test    al,FB_OVERLAP
        jz      @F
        mov     si,PtrDataOFFSET rdOverlap
        call    and_from_save
@@:

        ret

and_into_work   endp
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         = SI --> RECT_DATA structure to use                                          
;*                 ES = EGAMem
;*                 DS = PtrData
;*                 EGA programmed for AND mode
;*                 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

and_from_screen proc    near

        .errnz  pd_rd                   ;Must be at offset 0

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

        imul    bx,[si].rd_ptsScreen.pts_y,SCREEN_CBSCAN
        add     bx,[si].rd_ptsScreen.pts_x
        mov     cx,[si].rd_sizb
        .errnz  sizb_cy-sizb_cx-1

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

        xor     ax,ax
        mov     dx,[si].rd_ptbWork      ;Get origin in work area
        xchg    al,dh                   ;AX = ptbWork.ptb_y, DX = ptbWork.ptb_x
        add     dx,ax                   ;*1 + X component
        add     ax,ax                   ;*2
        add     ax,ax                   ;*4
        add     ax,dx                   ;*5 + X = start from work/mask
        xchg    ax,di
        .errnz  WORK_WIDTH-5

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

        mov     si,npAndXor             ;Set address of AND mask
        add     si,di
        add     di,offset abPointerWork
        sub     bx,si

;/*
;** 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
;*/

        mov     al,cl
        cbw
        mov     bp,WORK_WIDTH
        sub     bp,ax
        mov     dx,SCREEN_CBSCAN-WORK_WIDTH
        add     ax,ax
        add     ax,ax
        neg     ax
        add     ax,PtrCodeOFFSET and_from_screen_width_0
        jmp     ax                      ;Enter following code
        .errnz  WORK_WIDTH-5
        errnz   and_from_screen_width_0-and_from_screen_width_1-4
        errnz   and_from_screen_width_1-and_from_screen_width_2-4
        errnz   and_from_screen_width_2-and_from_screen_width_3-4
        errnz   and_from_screen_width_3-and_from_screen_width_4-4
        errnz   and_from_screen_width_4-and_from_screen_width_5-4

;/*
;** Register usage for the loop will be:
;**
;**       AX     =  loop starting address
;**       BX     =  offset off of SI to the screen source
;**       CL     =
;**       CH     =  height
;**       DX     =  delta to next scan of the screen source
;**       DS:SI --> AND mask
;**       ES:DI --> Destination in work area
;**       BP     =  offset to next byte of AND mask, next scan of work area
;*/

and_from_screen_width_5:
        cmp     al,es:[bx][si]          ;Load latches from work area
        movsb                           ;AND from screen into work area
and_from_screen_width_4:
        cmp     al,es:[bx][si]
        movsb
and_from_screen_width_3:
        cmp     al,es:[bx][si]
        movsb
and_from_screen_width_2:
        cmp     al,es:[bx][si]
        movsb
and_from_screen_width_1:
        cmp     al,es:[bx][si]
        movsb
and_from_screen_width_0:
        add     si,bp                   ;--> next AND mask
        add     di,bp                   ;--> next destination
        add     bx,dx                   ;--> next source
        dec     ch
        jz      @F
        jmp     ax                      ;Next pass
@@:
        ret

and_from_screen endp
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         = SI --> RECT_DATA structure to use
;*                 ES = EGAMem
;*                 DS = PtrData
;*                 EGA programmed for AND mode
;*                 EGA programmed for AND mode
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** 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

        assumes ds,PtrData
        assumes es,EGAMem

and_from_save   proc    near

        .errnz  pd_rd

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

        mov     ax,[si].rd_ptbWork
        mov     cx,[si].rd_ptbSave
        .errnz  ptb_y-ptb_x-1
        mov     dx,[si].rd_sizb
        .errnz  sizb_cy-sizb_cx-1
        mov     bl,cl
        add     bl,dl
        sub     bl,SAVE_BUFFER_WIDTH    ;BL = is overhang
        jle     and_from_save_do_a_pass ;No overhang

        sub     dl,bl                   ;Set X extent for pass 1
        mov     bh,dl                   ;Need it for pass 2
        push    si                      ;Must keep rectangle pointer around
        push    bx
        call    and_from_save_do_a_pass ;Process first half
        pop     bx
        pop     si
        mov     ax,[si].rd_ptbWork
        mov     cx,[si].rd_ptbSave
        .errnz  ptb_y-ptb_x-1
        mov     dx,[si].rd_sizb
        .errnz  sizb_cy-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

and_from_save_do_a_pass:

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

        xor     bx,bx
        xchg    ah,bl
        add     ax,bx                   ;*1 + x
        add     bx,bx                   ;*2
        add     bx,bx                   ;*4
        add     bx,ax                   ;*5 + x
        lea     di,abPointerWork[bx]    ;--> destination in work area
        mov     si,npAndXor
        add     si,bx                   ;--> AND mask

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

        xor     bx,bx
        xchg    bl,ch                   ;BX = ptbSave.ptb_y, CX = ptbSave.ptb_x
        shl     bx,3
        add     cx,bx                   ;CX = offset in save area
        .errnz  SAVE_BUFFER_WIDTH-8

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

        mov     bx,SAVE_BUFFER_WIDTH
        xchg    bl,dl
        neg     bx                      ;-width
        lea     ax,[bx][WORK_WIDTH]

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

        add     bx,bx                   ;*-2
        add     bx,bx                   ;*-4
        add     bx,PtrCodeOFFSET and_from_save_width_0
        errnz   and_from_save_width_0-and_from_save_width_1-4
        errnz   and_from_save_width_1-and_from_save_width_2-4
        errnz   and_from_save_width_2-and_from_save_width_3-4
        errnz   and_from_save_width_3-and_from_save_width_4-4
        errnz   and_from_save_width_4-and_from_save_width_5-4
        errnz   WORK_WIDTH-5

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

        xchg    bx,cx
        lea     bp,abPointerSave[bx]    ;Set correct save buffer address
        sub     bp,si                   ;Want to use [SI][BP]
        jmp     cx                      ;Enter following code

;/*
;** Register usage for the loop will be:
;**
;**       AX     =  delta to next scan of AND mask and work area
;**       BX     =  offset in save area of scan start
;**       CX     =  loop address
;**       DL     =  SAVE_BUFFER_WIDTH
;**       DH     =  height
;**       DS:SI --> AND mask
;**       ES:DI --> Destination in work area
;**       BP     =  delta from SI to next byte of save buffer
;*/

and_from_save_width_5:
        cmp     al,es:[bp][si]          ;Load latches from screen
        movsb                           ;AND to the work area
and_from_save_width_4:
        cmp     al,es:[bp][si]
        movsb
and_from_save_width_3:
        cmp     al,es:[bp][si]
        movsb
and_from_save_width_2:
        cmp     al,es:[bp][si]
        movsb
and_from_save_width_1:
        cmp     al,es:[bp][si]
        movsb
and_from_save_width_0:
        dec     dh
        jz      and_from_save_done
        add     si,ax                   ;--> next AND mask
        add     di,ax                   ;--> next save buffer scan
        add     bl,dl                   ;--> compute address of next
        lea     bp,abPointerSave[bx]    ;  scan in the work area
        sub     bp,si                   ;Want to use SI for incrementing
        jmp     cx

and_from_save_done:
        ret

and_from_save   endp
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
;*                 Calls:
;*                       copy_save_to_screen
;*                       copy_screen_to_save
;*
;* INPUT         = AL = fbFlush    
;*                 ES = EGAMem
;*                 DS = PtrData
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,EGAMem

copy_things_around proc near

;/*
;**  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    ax,cx
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,GRAF_BIT_MASK
        out16   dx,ax                   ;Disable all bits
        xchg    ax,cx

;/*
;** 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.
;*/

        mov     si,nppdOld
        test    al,FB_OLD_PTR
        jnz     cta_copy_from_save      ;The old rectangle goes
        test    al,FB_FLUSH_X
        jz      @F
        mov     si,PtrDataOFFSET rdFlushX
        call    copy_save_to_screen
        mov     al,fbFlush
@@:
        test    al,FB_FLUSH_Y
        jz      @F
        mov     si,PtrDataOFFSET rdFlushY
cta_copy_from_save:
        call    copy_save_to_screen
@@:

;/*
;** 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,fbAndRead
        mov     si,nppdNew
        test    al,FB_NEW_PTR
        jnz     cta_copy_to_save
        mov     si,PtrDataOFFSET 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
        mov     si,PtrDataOFFSET rdReadX
        call    copy_screen_to_save
        mov     al,fbAndRead
@@:
        test    al,FB_READ_Y
        jz      @F
        mov     si,PtrDataOFFSET rdReadY
cta_copy_to_save:
        call    copy_screen_to_save
@@:
        ret

copy_things_around      endp
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         = SI --> RECT_DATA structure to use 
;*                 ES = EGAMem
;*                 DS = PtrData
;*                 EGA programmed for COPY mode
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** 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

        assumes ds,PtrData
        assumes es,EGAMem

copy_save_to_screen proc near

        .errnz  pd_rd

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

        imul    di,[si].rd_ptsScreen.pts_y,SCREEN_CBSCAN
        add     di,[si].rd_ptsScreen.pts_x
        mov     dx,[si].rd_sizb
        .errnz  sizb_cy-sizb_cx-1
        mov     al,dl
        cbw
        mov     bp,SCREEN_CBSCAN
        sub     bp,ax

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

        mov     cx,[si].rd_ptbSave
        .errnz  ptb_y-ptb_x-1
        xor     bx,bx
        xchg    bl,ch                   ;CH = 0 for looping later
        shl     bx,3
        .errnz  SAVE_BUFFER_WIDTH-8
        add     bx,cx

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

        mov     al,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:
;**       ES:DI --> destination
;**       BP     =  delta to next destination scan
;**       DL     =  width of copy
;**       DH     =  height of copy
;**       BX     =  offset into the save buffer
;**       AL     =  SAVE_BUFFER_WIDTH
;*/
 
copy_save_next_scan:
        lea     si,abPointerSave[bx]
        mov     cl,dl
        rep     movs byte ptr es:[di],byte ptr es:[si]
        add     bl,al
        add     di,bp
        dec     dh
        jnz     copy_save_next_scan
        ret

;/*
;** The copy will wrap, so we have to handle it as two copies
;**
;** Currently:
;**       ES:DI --> destination
;**       BP     =  delta to next destination scan
;**       DL     =  width of copy
;**       DH     =  height of copy
;**       BX     =  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:
        lea     si,abPointerSave[bx]
        mov     cl,dl
        rep     movs byte ptr es:[di],byte ptr es:[si]
        sub     si,SAVE_BUFFER_WIDTH
        mov     cl,ah
        rep     movs byte ptr es:[di],byte ptr es:[si]
        add     bl,al
        add     di,bp
        dec     dh
        jnz     copy_save_next_scan_wrap
        ret                             ;ONE OF TWO RETS!

copy_save_to_screen endp
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 
;*                  ES = EGAMem
;*                  DS = PtrData
;*                  EGA programmed for COPY mode
;*                  EGA programmed for COPY mode      
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;** 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

        assumes ds,PtrData
        assumes es,EGAMem

copy_screen_to_save proc near

        .errnz  pd_rd

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

        imul    di,[si].rd_ptsScreen.pts_y,SCREEN_CBSCAN
        add     di,[si].rd_ptsScreen.pts_x
        mov     dx,[si].rd_sizb
        .errnz  sizb_cy-sizb_cx-1
        mov     al,dl
        cbw
        mov     bp,SCREEN_CBSCAN
        sub     bp,ax

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

        mov     cx,[si].rd_ptbSave
        .errnz  ptb_y-ptb_x-1
        xor     bx,bx
        xchg    bl,ch                   ;CH = 0 for looping later
        shl     bx,3
        .errnz  SAVE_BUFFER_WIDTH-8
        add     bx,cx

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

        xchg    di,si                   ;It helps to have the source in SI
        mov     al,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:
;**       ES:SI --> source
;**       BP     =  delta to next source scan
;**       DL     =  width of copy
;**       DH     =  height of copy
;**       CH     =  0
;**       BX     =  offset into the save buffer
;**       AL     =  SAVE_BUFFER_WIDTH
;*/

copy_screen_next_scan:
        lea     di,abPointerSave[bx]
        mov     cl,dl
        rep     movs byte ptr es:[di],byte ptr es:[si]
        add     bl,al
        add     si,bp
        dec     dh
        jnz     copy_screen_next_scan
        ret

;/*
;** The copy will wrap, so we have to handle it as two copies
;**
;** Currently:
;**       ES:SI --> source
;**       BP     =  delta to next source scan
;**       DL     =  width of copy
;**       DH     =  height of copy
;**       CH     =  0
;**       CL     =  amount of overflow
;**       BX     =  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:
        lea     di,abPointerSave[bx]
        mov     cl,dl
        rep     movs byte ptr es:[di],byte ptr es:[si]
        sub     di,SAVE_BUFFER_WIDTH
        mov     cl,ah
        rep     movs byte ptr es:[di],byte ptr es:[si]
        add     bl,al
        add     si,bp
        dec     dh
        jnz     copy_screen_next_scan_wrap
        ret                             

copy_screen_to_save endp

        public  compute_rects
        public  clip_rects
        public  do_clipping
        public  xor_to_screen
        public  and_into_work
        public  and_from_screen
        public  and_from_save
        public  copy_things_around
        public  copy_save_to_screen
        public  copy_screen_to_save


sEnd    PtrCode
end
