;*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 = CURSORS.ASM
;*
;* DESCRIPTIVE NAME = Common routines and data definitions from CURSORS.ASM 
;*
;*
;* VERSION      V2.0
;*
;* DATE         08/22/88
;*
;* DESCRIPTION 
;*                                                                 
;*      This module contains the routines which are required to manage
;*      the pointer image.  The actual pointer drawing primitive reside
;*      elsewhere.
;*
;*      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.                                       
;*                                                                           
;*      Logically, the pointer image isn't part of the physical display      
;*      surface.  When a drawing operation coincides with the pointer        
;*      image, the result is the same as if the pointer image wasn't         
;*      there.  In reality, if the pointer image is part of the display      
;*      surface it must be removed from memory before the drawing            
;*      operation may occur, and redrawn at a later time.                    
;*                                                                           
;*      This exclusion of the pointer image is the responsibility of         
;*      the display driver.  If the pointer image is part of physical        
;*      display memory, then all output operations must perform a hit        
;*      test to determine if the pointer must be removed from display        
;*      memory, and set a protection rectangle wherein the pointer must      
;*      not be displayed.  The actual pointer image drawing routine          
;*      must honor this protection rectangle by never drawing the            
;*      pointer image within its boundary.                                   
;*                                                                           
;*      Restrictions:
;*                                                                           
;*      The Window Manager is responsible for mapping generic pointers       
;*      into the correct size for the driver, as specified in the            
;*      display driver's resources.                                          
;*                                                                           
;*      The segments containing these routines and their data must be fixed  
;*      in memory since they are called at interrupt time.                   
;*                                                                           
;*      The exclusion rectangle is a serially reusable resource.  Only
;*      one may be defined at any given moment.  There is no check for       
;*      this.  It is assumed that there will never be contention for it      
;*      since         is non-preemptive.                                     
;*
;*      Public Data:          MyCodeData  (copy of Data in PtrCode) 
;*                            PtrCodeData                           
;*
;* FUNCTIONS    Public:  MoveCursor
;*                       exclude
;*                       exclude_test
;*                       inner_exclude_test
;*                       unexclude
;*                       CheckCursor
;*                       DeviceSetCursor
;*                       SetColorCursor
;*                       set_device_cursor
;*                       starlight_disable
;*                       starlight_enable 
;*                       starlight_running
;*                       far_slite_disable
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/15/87                     Walt Moore [waltm] Created
;*   06/02/87                     Walt Moore [waltm]              
;*                                   Removed pushf/popf macros    
;*   06/02/87                     Walt Moore [waltm]              
;*                                  All routines who call this procedure 
;*                                  have DS correctly set.
;*   11/11/87                     Mike Harrington [mikehar]
;*                                    Incorporated DeviceSetCursor directly 
;*                                    into (and replacing).  Set cursor to 
;*                                    eliminate the scaffolding code in 
;*                                    DeviceSetCursor.
;*   04/13/88                     Walt Moore      [waltm]
;*                                  Removed push/pop SI which was
;*                                  corruption the screen_busy flag,
;*                                  and also trashing SI.
;*   01/16/89                     Waltm Moore [waltm]  Handle return 
;*                                    code from move_pointers (PTR HM00371)
;*   01/18/89                     Bob Grudem [bobgru]
;*                                  Changed call to exclude_test 
;*                                  to use jmp instead.
;*   01/18/89                     Bob Grudem [bobgru]
;*                                  Made it return by jumping back 
;*                                  through DX rather than using ret.
;*                                  This eliminates risk of page fault 
;*                                  on stack (however tiny) which
;*                                  would be     if we've been reached 
;*                                  from inside a critical section.
;*   02/06/89                     Hock Lee [hockl] Wrote SetColorCursor
;*   02/13/89                     Hock Lee [hockl]
;*                                  Modified to call set_device_cursor.
;*   02/16/93  @24668             T Kirschner [terryki]  Register 
;*                                  corruption fix: starlight_disable()
;*
;*****************************************************************************/

INCL_DDIMISC            equ     1
        include cursors.inc
        include assert.mac
        include njmp.mac
        include iodelay.inc
ifdef SLITE
        include egafam.inc                                                 $003
endif

        externFP GetDriverInfo
        externFP far_leave_driver           ;ENTER.ASM
        externFP far_enter_driver           ;ENTER.ASM
        externFP far_enter_driver_sem       ;ENTER.ASM

sBegin  Data
        externW cPtrSizes
sEnd    Data

sBegin  PtrData

;/*
;** The next set of PUBLICs is for CURSORS.ASM's use
;*/

        public  cxPointer
        public  cyPointer
        public  ptr_flags
        public  hot_x
        public  hot_y
        public  real_x
        public  real_y

ifdef SLITE
        globalB fStarlight,0    ; Starlight Present/Enabled flags          $001
                                ; BIT 0 = 1 if Starlight is present        $001
                                ; BIT 1 = 1 if Starlight is enabled        $001
                                ; BIT 2 = 1 if Starlight is being          $027
                                ; programmed, but not started              $027
endif
;/*
;** cxPointer is the width of the pointer in bits.  The size must be a
;** correct multiple for the PTR_ROUND_* values given above (e.g. if
;** rounding to byte boundaries, then this must be a multiple of 8.
;**
;** cyPointer is the height of the pointer in scans.
;**
;** These values are returned from the move_pointers routine.
;*/

cxPointer       dw      PTR_WIDTH_BITS  ;-1 to cause great problems if not
cyPointer       dw      PTR_HEIGHT      ;  defined

;/*
;**     (x_cell,y_cell) is the location of the pointer on the screen.
;**     These locations are only updated whenever a pointer is drawn.
;**
;**     (real_x,real_y) is the location of the pointer as specified
;**     by the user.  These locations are always kept current.
;**
;**     These cells may not be the same if the pointer drawing takes
;**     a lot of time and the mouse is moving quickly.  Therefore,
;**     after a pointer has been drawn, a check must be made to see
;**     if the pointer has moved, and if so the pointer must be drawn
;**     again.
;*/
 
        externA INIT_POINTER_X          ;initial x position
        externA INIT_POINTER_Y          ;initial y position

staticW x_cell,INIT_POINTER_X           ;x_cell of last drawn pointer
staticW y_cell,INIT_POINTER_Y           ;y_cell of last drawn pointer
staticW real_x,INIT_POINTER_X           ;Real x location of pointer
staticW real_y,INIT_POINTER_Y           ;Real x location of pointer

;/*
;**     hot_x and hot_y contain the hot spot adjustment for the pointer.
;**
;**     These locations should be zeroed whenever a pointer is changing
;**     or has been turned off, and should be set once a pointer has
;**     been defined.  When the pointer is turned off, the hot spot
;**     adjustment should be added back to the real pointer coordinates
;**     (real_x, real_y).  When a pointer is set, they should be
;**     subtracted off.  This will keep the pointer based at the hot
;**     spot during a change instead of the upper left corner.
;*/
 
hot_x    dw      0                      ;X hot spot adjustment
hot_y    dw      0                      ;Y hot spot adjustment

;/*
;**     screen_busy is a flag used for critical section code to
;**     indicate that the screen is busy.  Since pointer operations
;**     take a very long time (e.g. drawing a pointer), the screen_busy
;**     flag is set to 0 to show that the screen is busy, and then
;**     interrupts are enabled to allow other interrupts.  The basic
;**     operation for the semaphore is:
;**
;**             xor     cx,cx
;**             xchg    screen_busy,cx
;**             jcxz    operation_in_progress
;*/

                public  screen_busy
screen_busy     db      NOT_BUSY        ;Show screen not busy

;/*
;**     ptr_flags contains control flags indicating the pointer status.
;**     Flags are defined for the pointer being off, and the pointer being
;**     excluded.
;*/

ptr_flags       db      PTR_OFF         ;Pointer status, initially hidden

;/*
;**     The following words contain the bounding rectangle wherein the
;**     pointer is not allowed to be displayed.  The values for left and
;**     right will always be rounded to contain the entire byte (or
;**     word or dword if working to those boundaries).
;**
;**     These values will only be valid if exclude_count is non-zero.
;**
;**     NOTE: Only one rectangle at a time may be set.
;*/

                even
exclude_left    dw      0               ;left side  of exclusion rectangle
exclude_top     dw      0               ;top        of exclusion rectangle
exclude_right   dw      0               ;right side of exclusion rectangle
exclude_bottom  dw      0               ;bottom     of exclusion rectangle

exclude_count   db      RECT_NOT_PRES   ;Set non-zero if rectangle is valid

        globalW wPrevFlags,0
sEnd    PtrData



sBegin  PtrCode
        assumes cs,PtrCode

;/***************************************************************************
;*
;* FUNCTION NAME = MoveCursor
;*
;* DESCRIPTION   = Move Pointer To Given Coordinate 
;*       This is a private entry point within the display driver for the     
;*       Window Manager.  It also serves as an entry point to CheckCursor    
;*       if abs_x and abs_y are both 0x8000.                                 
;*                                                                           
;*       The current pointer location is set to the given coordinates.       
;*       If the pointer is visible, it will be moved to the new location.    
;*       If the pointer is off (a NULL pointer), or if the pointer has been  
;*       excluded, then just the pointer location will be updated.           
;*                                                                           
;*       If the pointer is on and the new pointer position will cause the    
;*       pointer to be excluded, it will be removed from the screen.         
;*
;*       Warnings:                                                      
;*             The word immediately preceeding MoveCursors is the       
;*             selector for the segment which contains all of the       
;*             data required for pointer drawing.  This segment will    
;*             be locked in place so it can be called at interrupt      
;*             time.                                                    
;*                                                                      
;*             This routine must not be a NODATA routine.  The ring 0   
;*             interrupt handler will examine the first instruction     
;*             looking for a MOV AX,xxxxH and skip it if it's the       
;*             first instruction, passing the ring 0 selector in AX     
;*
;*       Registers Preserved:       
;*             SI,DI,DS,BP          
;*       Registers Destroyed:       
;*             AX,BX,CX,DX,ES,FLAGS 
;*       Calls:                     
;*             exclude_test         
;*             pointer_off
;*             draw_pointer
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;* PSEUDO-CODE
;*       void MoveCursor(abs_x,abs_y)                                                     
;*       SWORD abs_x;                          // x coordinate of pointer                 
;*       SWORD abs_y;                          // y coordinate of pointer                 
;*       {                                                                                
;*         WORD    old_busy;                                                              
;*                                                                                        
;*         enter_crit();                       // Updating the real X,Y is                
;*         real_x = abs_x - hot_x;             //   a critical section                    
;*         real_y = abs_y - hot_y;                                                        
;*         old_busy = IS_BUSY;                 // Try for screen semephore                
;*         swap(screen_busy,old_busy);                                                    
;*         leave_crit();                                                                  
;*                                                                                        
;*         if (old_busy == NOT_BUSY)                                                      
;*         {                                                                              
;*             while(pointer positions disagree)                                          
;*             {                                                                          
;*                 if (pointer hidden || already excluded)                                
;*                 {                                                                      
;*                     screen_busy = NOT_BUSY;                                            
;*                     return();                                                          
;*                 }                                                                      
;*                 if (newly excluded)                                                    
;*                 {                                                                      
;*                     ptr_flags = PTR_EXCLUDED;                                          
;*                     pointer_off();                                                     
;*                     screen_busy = NOT_BUSY;                                            
;*                     return();                                                          
;*                 }                                                                      
;*                 draw_pointer(x_cell,y_cell);// can actually draw pointer               
;*             }                                                                          
;*             screen_busy = NOT_BUSY;         // others can have the screen now          
;*         }                                                                              
;*         return();                                                                      
;*       }                                                                                
;*
;**************************************************************************/

        assumes ds,nothing              
        assumes es,nothing              

        public  MyCodeData
        public  PtrCodeData

MyCodeData      dw      DataBASE        
PtrCodeData     dw      PtrDataBASE     
        errn$   MoveCursor              

cProc   MoveCursor,<FAR,PUBLIC,NO_ATTRPROF>,<si,di>
        parmW   abs_x                   ;x coordinate of pointer
        parmW   abs_y                   ;y coordinate of pointer
cBegin  <nogen>                         ;Same frame as CheckCursor

        mov     ax,PtrDataBASE
;/* 
;**    Ring 0 interrupt code will enter here.
;*/ 
        push    bp
        mov     bp,sp
        push    ds
        mov     ds,ax
        assumes ds,PtrData
        push    si
        push    di

        mov     ax,abs_x                ;AX = abs_x
        mov     bx,abs_y                ;BX = abs_y
        cmp     ax,bx                   ;CheckCursor iff abs_x == abs_y == 8000h
        jnz     @F
        cmp     ax,8000h
        jnz     @F
        jmp     Check_cursor_begin
@@:

;/*
;** some day we'll have to call the semaphore code to enter in here!!!
;** it would be nice if we had a separate entry point for a user
;** calling us.
;*/

        cli                             ;Moving is a critical section
        sub     ax,hot_x                ;Get the new pointer position,
        mov     real_x,ax               ;  relative to its hot spot
        sub     bx,hot_y
        mov     real_y,bx

;/*
;** $003 --- Begin Insertion ---
;*/

ifdef SLITE
        test    [fStarlight],00000100b  ;is the Blt engine being programmed? $027
        jnz     move_cursor_40          ;yes--don't mess with it           $027
        test    [fStarlight],00000010b  ;is the Blt engine on?
        jz      mc_slite_off            ;no--never mind
        push    ax                      ;save the X value
        mov     dx,SL_BCommand1
        in      al,dx
        test    al,BC1_SS
        pop     ax
        jnz     move_cursor_40
        call    starlight_disable       ;disable starlight
mc_slite_off:
endif

;/*
;**   $003 --- End Insertion ---
;*/

        xor     cx,cx                   ;Show that the screen is busy
        xchg    screen_busy,cl          ;  and get the old status
        errnz   IS_BUSY

move_cursor_10:
        sti                             ;Can allow interrupts now

;/*
;**     The real pointer (x,y) has been updated and the screen_busy
;**     flag has been set, and the old value of screen_busy is
;**     ready for testing.
;**
;**     If the screen previously wasn't busy, then if the pointer is
;**     hidden or excluded, no action will take place.  If the
;**     pointer isn't hidden or excluded, then the pointer will be
;**     drawn at it's new location if the hit test of the exclusion
;**     area succeeds.  If the hit test of the exclusion area fails,
;**     then the pointer will be taken down and left down.
;**
;**     If the screen was busy, the real pointer (x,y) will have
;**     been updated so that when the pointer is drawn next, it
;**     will have the current (x,y) to draw at.  The actions
;**     which could cause the screen to be busy are the following:
;**
;**         Pointer is being taken down by the SetCursor routine:
;**
;**             If this is the case, then the pointer is to remain
;**             off until a new pointer is selected, at which time
;**             the timer code will try to bring the pointer back.
;**
;**
;**         Pointer is being taken down by exclusion from an output
;**         function:
;**
;**             If this is the case, then the pointer will remain
;**             excluded until either one of two events occur:
;**
;**                 a new pointer is selected, at which time the
;**                 timer code will try to bring the pointer back.
;**
;**                 The timer interrupt occured and determined that
;**                 the pointer needed to be unexcluded ([screen_busy]
;**                 must be false for the timer to redraw the pointer).
;**
;**         Pointer is being drawn by MoveCursor:
;**
;**             Hey, thats us!  If this is the case, after the pointer
;**             has been drawn, the real pointer location will be checked
;**             to see if the pointer has moved, and if it has, it will
;**             be drawn again at the correct spot (unless it has become
;**             excluded).
;*/

        jcxz    move_cursor_40          ;Screen is busy, just update x,y
        errnz   IS_BUSY
        mov     x_cell,ax               ;Set x,y where drawing will occur
        mov     y_cell,bx

;/*
;**     The following call to exclude_test need not be made from here
;**     with interrupts disabled since screen_busy has been set, and no
;**     code will modify the screen while screen_busy is set (except the
;**     normal drawing routines which cannot be called at interrupt time,
;**     who just happen to set up the exclusion rectangle, which should
;**     never be able to be called while screen_busy is set, ...)
;*/

        mov     cl,PTR_OFF+PTR_EXCLUDED+PTR_REDRAW ;Set flag mask for exclude test
        call    exclude_test            ;Go see if excluded
        jnc     move_cursor_20          ;Hidden or already excluded
        jz      move_cursor_30          ;Can draw the pointer

;/*
;**     The hit test was positive.  The pointer must be excluded.  Since
;**     this is a pointer drawing operation which will take some time,
;**     do it with interrupts enabled.
;*/

        or      ptr_flags,PTR_EXCLUDED  ;Show pointer excluded
        call    pointer_off             ;Remove pointer
        assumes ds,PtrData
        assumes es,nothing

move_cursor_20:
        mov     screen_busy,NOT_BUSY    ;Show screen no longer busy
        jmp     short move_cursor_40    ;Exit

;/*
;**     The hit test was negative and the pointer was on and not
;**     excluded.  Draw the pointer at the passed location.  Since
;**     this is a pointer drawing operation which will take some time,
;**     do it with interrupts enabled.
;*/

move_cursor_30:
        cCall   draw_pointer,<x_cell,y_cell>
        assumes ds,PtrData
        assumes es,nothing

;/*
;**     The pointer has been drawn.  Check to see if the pointer
;**     has moved again while being drawn.  If it has, then draw
;**     the pointer at the new location.  Otherwise make the screen
;**     available.
;*/

        cli                             ;This is a critical section!
        mov     cl,NOT_BUSY             ;Show screen available to move_cursor_10
        mov     ax,real_x               ;Get the real X location
        mov     bx,real_y
        cmp     ax,x_cell               ;If real X is different then pointer's
        jne     move_cursor_10          ;  X, then go draw the pointer
        cmp     bx,y_cell               ;If real Y is different then pointer's
        jne     move_cursor_10          ;  Y, then go draw the pointer
        mov     screen_busy,cl          ;Show screen available
        sti                             ;Done with this critical section

move_cursor_40:
        cli                                ;macro fudge
        sti                                ;just in case PJW

cEnd

page

;/***************************************************************************
;*
;* FUNCTION NAME = Exclude 
;*
;* DESCRIPTION   = 
;*        
;*       The pointer exclusion area is set to the passed value.  If the            
;*       pointer image is currently within the excluded area, it will be           
;*       removed from the screen.                                                  
;*                                                                                 
;*       The given X coordinates will be rounded to the word size being            
;*       used (byte, word, dword).  This removes the adjustment for word           
;*       size out of the actual hit test code into the code that creates           
;*       the exclusion rectangle.  The left side will be rounded down and          
;*       the right side will be rounded up).                                       
;*                                                                                 
;*       As an example, for bytes:                                                 
;*                                                                                 
;*              top,left  ---------------------                                    
;*                       |                     |                                   
;*                       |                     |                                   
;*                       |                     |                                   
;*                       |       SCREEN        |                                   
;*                       |                     |                                   
;*                       |                     |                                   
;*                       |                     |                                   
;*                        ---------------------  right,bottom                      
;*                                                                                 
;*                                                                                 
;*              |L              |               |              R|                  
;*            |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|                
;*                                                                                 
;*               left will always be at D7                                         
;*               right will always be at D0                                        
;*                                                                                 
;*                                                                                 
;*       Exclude is not called by the interrupt pointer drawing code.              
;*
;*       Registers Preserved:                  
;*             BP                              
;*       Registers Destroyed:                  
;*             AX,BX,CX,DX,SI,DI,DS,ES,FLAGS   
;*       Calls:                                
;*             pointer_off                     
;*             exclude_test                    
;*       Warnings:                             
;*             interrupts enabled upon exit    
;*        
;* INPUT         = (cx) = left               
;*                 (dx) = top                
;*                 (si) = right  (inclusive) 
;*                 (di) = bottom (inclusive) 
;*
;* OUTPUT        = None
;*
;* RETURN-NORMAL = DS = PtrData 
;* RETURN-ERROR  = None
;*
;* PSEUDO-CODE    =
;*    
;*    void exclude (left,top,right,bottom);
;*    SWORD left;                           // Left   pixel of exclusion rect
;*    SWORD top;                            // Top    pixel of exclusion rect
;*    SWORD right;                          // Right  pixel of exclusion rect
;*    SWORD bottom;                         // Bottom pixel of exclusion rect
;*    {
;*      WORD    old_state;
;*    
;*      round x coordinates to a boundary;  // round x boundaries
;*      save exclusion rectangle;           // save left, top, right, bottom
;*      enter_crit();                       // start of critical setcion
;*    
;*      if (pointer within exclusion rectangle)
;*      {
;*          old_state = screen_busy         // We'll restore this later
;*          screen_busy = IS_BUSY;          // Lock out all other routines
;*          ptr_flags = PTR_EXCLUDED;       // Show pointer as excluded
;*    
;*          // We now have exclusive control of the screen.  We have the
;*          // semephore so CheckCursor won't try to bring back the pointer,
;*          // and we have told MoveCursor that the pointer is excluded, so
;*          // it won't try to move it.
;*    
;*          enable_interrupts;              // Allow other things to happen
;*          pointer_off();
;*          screen_buys = old_state;
;*      }
;*      exit_crit();                        // End of critical section
;*    
;*    }
;*    
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   far_exclude,<FAR,PUBLIC,NONWIN,NODATA>,<DS>
cBegin
        mov     ds,PtrCodeData          ;Need access to our data segment
        assumes ds,PtrData

        and     cx,PTR_ROUND_LEFT       ;Round left coordinate down
        or      si,PTR_ROUND_RIGHT      ;Round right coordinate up

        mov     bl,80h                  ;Make any negative top/lefts 0
        cmp     ch,bl                   ;'C' if positive number
        sbb     ax,ax
        and     cx,ax
        cmp     dh,bl
        sbb     ax,ax
        and     dx,ax

        mov     exclude_left,cx         ;Set up exclusion rectangle
        mov     exclude_top,dx
        mov     exclude_right,si
        mov     exclude_bottom,di
        mov     exclude_count,RECT_PRES
        mov     cl,PTR_OFF+PTR_EXCLUDED ;Set test mask for exclude_test

;$003 --- Begin Insertion ---
ifdef SLITE
@@:
        test    [fStarlight],00000100b  ;is the Blt engine being programmed? $027
        jnz     @B                      ;yes--wait till it's done            $027
        test    [fStarlight],00000010b  ;is the Blt engine on?
        jz      ex_slite_off            ;no--never mind
        mov     dx,SL_BCommand1
        in      al,dx                   ;wait until the Blt engine
        test    al,BC1_SS               ;is done
        jnz     @B
        cli                             ;keep pointer from interrupting
        test    [fStarlight],00000010b  ;is the Blt engine still on?
        jz      ex_slite_off            ;no--pointer must have turned it off
        call    starlight_disable       ;yes--disable starlight
ex_slite_off:
endif
;$003 --- End Insertion ---

;/*
;**     Inside the critical section, we'll jump to exclude_test rather
;**     than call, thus avoiding a stack reference (the push of the return
;**     address).  Currently (1 Mar 89) the ring 2 stack is 4K bytes and
;**     is locked down in memory.  If the stack size is ever increased
;**     without locking down more than the first page, then we will
;**      still be protected from stack page faults.
;*/

        sti                                ;macro fudge

        cli                             ;Hit test is critical code
        mov     ax,x_cell               ;Get current pointer x,y location
        mov     bx,y_cell
        mov     dx,PtrCodeOFFSET hit_test_return ;load return address
        jmp     short inner_exclude_test
hit_test_return:
        jnc     no_exclude_needed       ;Hidden or already excluded
        jz      no_exclude_needed       ;Don't need to exclude

;/*
;**     The pointer needs to be removed from the screen.  Show that
;**     the screen is busy and then take the pointer down.  Since this
;**     is a pointer drawing operation which will take some time, do
;**     it with interrupts enabled.
;*/

        xor     cx,cx
        xchg    screen_busy,cl          ;Set screen busy and save old state
        errnz   IS_BUSY
        mov     ptr_flags,PTR_EXCLUDED  ;Show pointer excluded
        sti                                ;Allow ints
        push    cx
        call    pointer_off             ;Remove pointer
        mov     ds,PtrCodeData          ;Restore our data segment
        assumes ds,PtrData

        pop     ax                      ;Restore screen busy state
        mov     screen_busy,al

no_exclude_needed:
        cli                                ;macro fudge
        sti

cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = exclude_test - Pointer Exclusion Test
;*
;* DESCRIPTION   =
;*
;*  The pointer is checked for its current status (excluded, hidden),
;*  and a hit test possibly made against the exclusion rectangle.
;*
;*  The ptr_flags are tested against the passed test mask, and if
;*  any of the test mask bits are set, then the hit test is skipped.
;*  This allows drawing code to check for either exclusion or the
;*  user turning off the pointer before a hit test is performed.  It
;*  also allow the timer code to just check for the user turning off
;*  the pointer, and if it hasn't been turned off, to go ahead and
;*  perform the hit test.
;*
;*  Registers Preserved:
;*        DX,SI,DI,BP,DS,ES
;*  Registers Destroyed:
;*        AX,BX,CX,FLAGS
;*  Calls:
;*        None
;*
;* INPUT         = AX = x_cell to hit against
;*                 BX = y_cell to hit against
;*                 CL = ptr_flags test mask
;*                 DX = return address (for inner_exclude_test)
;* OUTPUT        = None
;*
;* RETURN-NORMAL = 'C' clear if any bits in test mask are set in [ptr_flags]
;*                 'C' set if no bits in test mask are set in [ptr_flags]
;*                 'Z' clear if pointer is excluded
;*                 'Z' set if pointer isn't excluded
;* RETURN-ERROR  = None
;*
;* PSEUDO-CODE
;*
;*     flags exclude_test(x_cell,y_cell,ptr_flags_mask);
;*     SWORD x_cell;                             // X location of pointer
;*     SWORD x_cell;                             // Y location of pointer
;*     WORD  ptr_flags_mask;                     // Mask to test curs_flags with
;*     {
;*       if (ptr_flags & ptr_flags_mask)         // Skip hit test?
;*           return(NOCARRY)                     // Yes, show test skipped
;*     
;*       if (exclude_count)                      // If no exclusion rectangle
;*           return (CARRY,ZERO)                 //   show not excluded
;*     
;*       if (x_cell > exclude_right)             // If start is > right
;*           return(CARRY,ZERO)                  //   isn't exclude
;*     
;*       if (x_cell+cxPointer < exclude_left)    // If right of pointer < left
;*           return(CARRY,ZERO)                  //   isn't exclude
;*     
;*       if (y_cell > exclude_bottom)            // If top is > bottom
;*           return(CARRY,ZERO)                  //   isn't exclude
;*     
;*       if (y_cell+cyPointer<=exclude_bottom)   // If bottom of pointer <= top
;*           return(CARRY,ZERO)                  //   isn't exclude
;*     
;*       return (CARRY,NONZERO)                  // SHow excluded
;*     }
;**************************************************************************/

        assumes ds,PtrData
        assumes es,nothing

cProc   exclude_test,<NEAR,PUBLIC,NO_ATTRPROF>
cBegin  <nogen>
        pop     dx                      ;pop the return address into DX
cEnd    <nogen>

cProc   inner_exclude_test,<NEAR,PUBLIC,NO_ATTRPROF>
cBegin  <nogen>
        test    ptr_flags,cl            ;Do the hit test? (Clears 'C')
        jnz     exclude_test_20         ;Skip hit test
        xor     cx,cx                   ;Show pointer not excluded (need a 0)
        cmp     exclude_count,cl        ;Is there an exclusion area
        je      exclude_test_10         ;  No, show not excluded
        errnz   RECT_NOT_PRES

        cmp     ax,exclude_right        ;Is left of pointer > right of exclude?
        jg      exclude_test_10         ;  Yes, not excluded
        add     ax,cxPointer            ;Add in width of pointer or icon
        cmp     ax,exclude_left         ;Is right of pointer < left of exclude?
        jl      exclude_test_10         ;  Yes, not excluded

        cmp     bx,exclude_bottom       ;Is top of pointer > bottom of exclude
        jg      exclude_test_10         ;  Yes, not excluded
        add     bx,cyPointer            ;Add in height of pointer/icon
        cmp     bx,exclude_top          ;Is bottom of pointer < top of exclude
        jle     exclude_test_10         ;  Yes, not excluded
        inc     cx                      ;Show pointer is excluded

exclude_test_10:
        or      cx,cx                   ;Clear 'Z' if pointer is excluded
        stc                             ;Show some bits in mask were set

exclude_test_20:
        jmp     dx
cEnd    <nogen>
page

;/***************************************************************************
;*
;* FUNCTION NAME = UnExclude - Remove Exclusion Area
;*
;* DESCRIPTION   =
;*
;*   The exclusion rectangle is removed.  The pointer will not be redrawn
;*   since it might have to be taken down for the next call.  Redrawing
;*   the pointer will be left to the CheckCursor routine.
;*   Registers Preserved:
;*         AX,BX,CX,DX,Si,DI,BP
;*   Registers Destroyed:
;*         DS
;*   Calls:
;*         None
;*
;* INPUT         = None
;* OUTPUT        = None
;* RETURN-NORMAL = DS = PtrData
;* RETURN-ERROR  = None
;*
;* PSEUDO-CODE   =
;*       void unexclude();
;*       {
;*         exclude_count = RECT_NOT_PRES;
;*         return();
;*       }
;***************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   far_unexclude,<FAR,PUBLIC,NONWIN,NODATA>,<DS>
cBegin
        mov     ds,PtrCodeData
        assumes ds,PtrData

        mov     exclude_count,RECT_NOT_PRES
cEnd

page

;/***************************************************************************
;*
;* FUNCTION NAME = CheckCursor - Check on the Pointer 
;*
;* DESCRIPTION   = 
;*                 
;*     This is not an entry point.  The entry point is MoveCursor.
;*  
;*     The pointer is checked to see if it can be unexcluded, and if so
;*     it is unexcluded.  This is the only routine which can cause the
;*     pointer to become visible once it has become invisible (excluded
;*     by a drawing operation or turned off because of a new shape being
;*     set).
;*  
;*     It is expected that this routine be called at a rate close to once
;*     every quarter of a second.  This allows for a lazy redraw of the
;*     pointer whenever it has become excluded.
;*  
;*     If the screen is busy (due to a current pointer operation), the
;*     pointer is turned off, or it isn't excluded, then there is nothing
;*     for this routine to do.  If the pointer is excluded, then a hit test
;*     will be performed against the current exclusion rectangle (if there
;*     is one), and if the pointer is now visible, it will be drawn.
;*  
;*     Returns TRUE if successful (either there was nothing to do, or
;*     the cursor was successfully drawn.)
;*  
;*     Returns FALSE if the cursor should have been drawn but could not be,
;*     such as when it is excluded by an output operation in progress.
;*                                                                                 
;*     This routine is intended to be interrupt code.  The state of the            
;*     interrupts will be maintained over the entire call, but interrupts          
;*     will be enabled whenever the pointer is drawn.                              
;*                                                                                 
;*     For devices with hardware support for pointers, this routine could          
;*     be a nop.                                                                   
;*                                                                                 
;*     Registers Preserved:           
;*           SI,DI,DS,BP              
;*     Registers Destroyed:           
;*           AX,BX,CX,DX,ES,FLAGS     
;*     Calls:                         
;*           exclude_test             
;*           pointer_off              
;*           draw_pointer             
;*  
;* INPUT         = None
;* OUTPUT        =
;*
;* RETURN-NORMAL = DX:AX = BOOL
;* RETURN-ERROR  = None
;*
;* PSEUDO-CODE   =
;*
;*     BOOL CheckCursor();
;*     {
;*       WORD    old_busy;
;*    
;*       if (swap(screen_busy,old_busy) == screen_busy)
;*           return(FALSE);                  // cannot access the screen
;*    
;*       if (pointer is off || pointer not excluded)
;*       {                                   // nothing to do
;*           screen_busy = NOT_BUSY;
;*           return(TRUE);
;*       }
;*    
;*       // The pointer is currently excluded.  If it is now unexcluded,
;*       // it must be drawn.
;*    
;*     test_if_unexcluded:
;*    
;*       enter_crit();
;*       if (pointer unexcluded)
;*       {
;*           leave_crit();
;*           draw_pointer(x_cell,y_cell)     // draw pointer at new location
;*           ptr_flags = 0;                  // show pointer is on and unexcluded
;*           enter_crit();
;*           if (pointer positions disagree)
;*               goto test_if_unexcluded;    // moved while we were drawing it.
;*           screen_busy = NOT_BUSY;
;*           leave_crit();
;*           return(TRUE);
;*       }
;*       leave_crit();
;*    
;*       // Must test to see if the pointer became excluded after we
;*       // just brought it back.
;*    
;*       if (pointer is excluded)
;*       {
;*           pointer_off();
;*           ptr_flags = PTR_EXCLUDED;
;*       }
;*    
;*       screen_busy = NOT_BUSY;             // others can have the screen now
;*       return(TRUE);
;*     }
;*
;**************************************************************************/

        assumes ds,PtrData              ;Set by MoveCursor
        assumes es,nothing              ;Have no idea what's in it

cProc   CheckCursor,<FAR,PUBLIC,NO_ATTRPROF>,<si,di>
        parmW   abs_x                   ;dummy
        parmW   abs_y                   ;dummy
cBegin  <nogen>                         ;Same frame as MoveCursor

Check_cursor_begin:

;/*
;** $003 --- Begin Insertion ---
;*/

ifdef SLITE
        test    [fStarlight],00000100b  ;is the Blt engine being programmed? $027
        njnz    check_cursor_exit       ;yes--don't mess with it.            $027
        test    [fStarlight],00000010b  ;is the Blt engine on?
        jz      cc_slite_off            ;no--never mind
        mov     dx,SL_BCommand1
        in      al,dx
        test    al,BC1_SS
        mov     ax,0                    ;Set screen busy, just in case
        jz        @F
        jmp        check_cursor_exit
@@:
        call    starlight_disable       ;disable starlight
cc_slite_off:
endif

;/*
;** $003 --- End Insertion ---
;*/

        xor     ax,ax                   ;Set screen busy
        xchg    screen_busy,al          ;  and get current status
        or      ax,ax
        njz     check_cursor_exit       ;Screen is busy, skip check, return AX=0
        errnz   IS_BUSY

        mov     al,ptr_flags            ;See if the pointer is on and excluded
        add     al,al
        jc      check_cursor_40         ;Pointer is off (NULL); no work to do
        add     al,al
        js      check_cursor_05         ;Pointer has changed and needs refreshing
        jnc     check_cursor_40         ;Pointer is on, but not excluded

        errnz   PTR_OFF-10000000b       ;Must be this bit
        errnz   PTR_EXCLUDED-01000000b  ;Must be this bit
        errnz   PTR_REDRAW-00100000b    ;Must be this bit

;/*
;**     The pointer is on and is currently excluded.  Perform a hit
;**     test against the current (real_x, real_y) pointer location,
;**     and if the pointer is no longer excluded, show the pointer.
;**     (Can also reach here if doing animated pointers and we need to draw
;**     the new pointer, we don't need to work about the old pointer because
;**     the new one will be full sized and draw pointer will remove it
;**     if they were in different positions)
;**
;**     After drawing the pointer, the exclusion test must be repeated.
;**     If the pointer is excluded, the mouse moved while drawing it.
;**     If this is the case, take it down, then wait for the next
;**     timer interrupt to try and put it back up.
;*/

check_cursor_05:
        cli                             ;Dont want an old X and a new Y!
        mov     ax,real_x               ;Get the X,Y for the test
        mov     bx,real_y

check_cursor_10:
        sti

        mov     si,ax                   ;Save these over the exclude test
        mov     di,bx                   ;  (exclude_test alters ax & bx)
        xor     cx,cx                   ;Don't test any flags
        call    exclude_test            ;Is pointer excluded?
        jnz     check_cursor_30         ;  Yes (might have to remove it now)

;/*
;**     The pointer is no longer excluded, so draw the new pointer.  Since
;**     this is a pointer drawing operation which will take some time, do
;**     it with interrupts enabled.
;*/

        mov     x_cell,si               ;Save (x,y) where pointer will be drawn
        mov     y_cell,di
        cCall   draw_pointer,<x_cell,y_cell>
        assumes ds,PtrData
        assumes es,nothing

        mov     ptr_flags,0             ;Pointer is on and not excluded

;/*
;**     The pointer has been drawn.  Check to see if the pointer
;**     has moved again while being drawn.  If it has, then draw
;**     the pointer at the new location.  Otherwise make the screen
;**     available.
;*/


        cli                             ;This is a critical section!
        mov     ax,real_x               ;Get the real pointer (X,Y) location
        mov     bx,real_y
        cmp     ax,x_cell               ;If real X is different than pointer's
        jne     check_cursor_10         ;  X, then go draw the pointer
        cmp     bx,y_cell               ;If real Y is different than pointer's
        jne     check_cursor_10         ;  Y, then go draw the pointer
        mov     screen_busy,NOT_BUSY    ;Show available before enabling
        sti                             ;  ints so MoveCursor can update
        jmp     short check_cursor_50

;/*
;**     The pointer is excluded.  If it became excluded after the pointer
;**     was brought up (while interrupts were enabled), it must be
;**     taken down again.  If it is taken down, then no attempt will
;**     be made to draw it again until the next timer interrupt.
;*/

check_cursor_30:
        test    ptr_flags,PTR_EXCLUDED  ;Is pointer excluded?
        jnz     check_cursor_40         ;  Yes, don't need to take it down
        call    pointer_off             ;Remove the pointer
        assumes ds,PtrData
        assumes es,nothing

        mov     ptr_flags,PTR_EXCLUDED  ;Pointer is now excluded

;/*
;**     Whatever was needed has been done.  Free the screen and exit
;*/

check_cursor_40:
        mov     screen_busy,NOT_BUSY

check_cursor_50:


        mov     ax,1                    ; return success

check_cursor_exit:
        cwd
cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = DeviceSetCursor - Set Current Pointer Shape (monochrome only) 
;*
;* DESCRIPTION   = 
;*
;*    This is a private entry point within the display driver for the
;*    Window Manager.
;* 
;*    The given pointer shape is saved in local storage to be used as
;*    the current pointer shape.  If the pointer to the pointer shape is
;*    NULL, then no pointer is to be drawn.
;* 
;*    The given pointer shape will have been converted from generic form
;*    into the dimensions requested by this driver.
;* 
;*    Any pointer on the screen will be removed before the new pointer
;*    shape is set.  The drawing of the new pointer will be delayed
;*    until the check_pointer routine is called (check_pointer is the
;*    only routine that can make the pointer become visible).
;* 
;*    If this routine is reentered, the request will be ignored.
;*
;*    Registers Preserved:
;*          SI,DI,DS,BP
;*    Registers Destroyed:
;*          AX,BX,CX,DX,ES,FLAGS
;*    Calls:
;*          set_device_cursor
;*          pointer_off
;*          move_pointers
;* 
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = 0            
;* RETURN-ERROR  = error logged 
;*
;**************************************************************************/

        assumes ds,nothing              
        assumes es,nothing              

        check   DeviceSetCursor,<hdc,pptlHotSpot,hbm,hddc,ulFunN>

cProc   DeviceSetCursor,<FAR,PUBLIC,NODATA>,<si,di>
        parmD   hdc
        parmD   lpXY
        parmD   hbmPointer
        parmD   hddc
        parmD   FunN
cBegin
        mov     ds,MyCodeData
        assumes ds,Data
        cCall   far_enter_driver_sem    ;Acquire screen semaphore

ifdef FIREWALLS
        fw_zero <es>
        mov     bx,hbmPointer.lo        ;They must have passed us a valid
        mov     dx,hbmPointer.hi        ;  bitmap handle else the Engine
        mov     cx,bx                   ;  messed up
        or      cx,dx
        jz      @F
        cmp     dx,HDBM_IDENT
        jne     dsc_not_bitmap
        cmp     [bx].bm_sd.sd_usId,SURFACE_IDENT
        jne     dsc_not_bitmap
        test    [bx].bm_sd.sd_fb,SD_DEVICE
        jz      @F
dsc_not_bitmap:
        rip     text,<DeviceSetCursor was given an invalid bitmap>
@@:
        fw_zero <es>
endif
        cmp     hbmPointer.lo,0         ;Look for null pointer
        jz      dsc_looking_good
        les     bx,lpXY
        assumes es,nothing
        mov     ax,es:[bx].ptl_x.lo
        cwd
        cmp     dx,es:[bx].ptl_x.hi
        jne     dsc_inv_coordinate
        mov     cx,ax                   ; CX = x hotspot
        mov     ax,es:[bx].ptl_y.lo     ; AX = y hotspot
        cwd
        cmp     dx,es:[bx].ptl_y.hi
        je      dsc_looking_good

dsc_inv_coordinate:
        mov     ax,PMERR_INV_COORDINATE
        save_error_code
        xor     ax,ax
        jmp     short dsc_exit

dsc_looking_good:
        cCall   set_device_cursor,<cx,ax,(hbmPointer.lo),0,0,(hddc.lo)>

dsc_exit:
        cwd
        cCall   far_leave_driver
        fw_zero <es,cx>
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = SetColorCursor
;*
;* DESCRIPTION   = 
;*
;*    This function is equivalent to SetCursor except that it takes a
;*    color bitmap handle.
;*   
;*    This is a private entry point within the display driver for the
;*    Window Manager.
;*   
;*    The given pointer shape is saved in local storage to be used as
;*    the current pointer shape.    If pptri is NULL, the pointer is to
;*    be removed.
;*   
;*    The given pointer shape will have been converted from generic form
;*    into the dimensions requested by this driver.
;*   
;*    Any pointer on the screen will be removed before the new pointer
;*    shape is set.  The drawing of the new pointer will be delayed
;*    until the check_pointer routine is called (check_pointer is the
;*    only routine that can make the pointer become visible).
;*
;*    Registers Preserved:       
;*          SI,DI,DS,BP          
;*    Registers Destroyed:       
;*          AX,BX,CX,DX,ES,FLAGS 
;*    Calls:                     
;*          set_device_cursor    
;*          GetDriverInfo        
;*
;* INPUT         = pptri = Pointer to POINTERINFO structure.
;* OUTPUT        = None
;*
;* RETURN-NORMAL = 1
;* RETURN-ERROR  = 0
;*                 error logged
;**************************************************************************/

        assumes ds,nothing              ;Set up by prologue
        assumes es,nothing              ;Have no idea what's in it

cProc   SetColorCursor,<FAR,PUBLIC,NODATA>,<si,di>
        parmD   hdc
        parmD   pptri
        parmD   hddc
        parmD   FunN
cBegin
        mov     ds,MyCodeData
        assumes ds,Data
        cCall   far_enter_driver_sem    ;Acquire screen semaphore

        mov     bx,pptri.lo
        mov     dx,pptri.hi
        mov     ax,bx
        or      ax,dx                   ;Removing cursor from screen?
        njz     scc_null_cursor         ;  Yes, DX = AX = 0

        mov     es,dx                   ;ES:BX -> ptri
        assumes es,nothing
        mov     di,es:[bx].ptri_hbmColor.hi
        mov     si,es:[bx].ptri_hbmColor.lo
        assert  di,NE,0                 ;Must be non null bitmaps
        assert  si,NE,0
        assert  es:[bx].ptri_hbmPointer.hi,NE,0
        assert  es:[bx].ptri_hbmPointer.lo,NE,0

        farPtr  Myhbm,<es:[bx].ptri_hbmPointer.hi>,<es:[bx].ptri_hbmPointer.lo>
        farPtr  MyIndex,0,DI_HBITMAP    ;Translate hbmPointer
        cCall   GetDriverInfo,<Myhbm,MyIndex,hdc>
        mov     cx,ax
        and     cx,dx
        inc     cx
        jz      scc_inv_cursor          ;Return error

ifdef FIREWALLS
        pusha
        assert  dx,E,HDBM_IDENT
        mov     bx,ax
        cmp     [bx].bm_sd.sd_usId,SURFACE_IDENT
        jne     scc_not_bm
        test    [bx].bm_sd.sd_fb,SD_DEVICE
        jz      @F
scc_not_bm:
        rip     text,<SetColorCursor was given an invalid bitmap>
@@:
        popa
endif

        xchg    si,ax                   ;SI = translated hbmPointer
        farPtr  Myhbm,di,ax             ;Translate hbmColor
        farPtr  MyIndex,0,DI_HBITMAP
        cCall   GetDriverInfo,<Myhbm,MyIndex,hdc>
        mov     cx,ax                   ;AX = translated hbmColor
        and     cx,dx
        inc     cx
        jnz     scc_good_color_cursor

scc_inv_cursor:
        mov     ax,PMERR_INV_CURSOR_BITMAP
        save_error_code
        xor     ax,ax
        jmp     short scc_exit

scc_good_color_cursor:

ifdef FIREWALLS
        pusha
        assert  dx,E,HDBM_IDENT
        mov     bx,ax
        cmp     [bx].bm_sd.sd_usId,SURFACE_IDENT
        jne     scc_not_bitmap
        test    [bx].bm_sd.sd_fb,SD_DEVICE
        jz      @F
scc_not_bitmap:
        rip     text,<SetColorCursor was given an invalid bitmap>
@@:
        popa
endif

        mov     dx,si                   ;DX = translated hbmPointer
        les     di,pptri
        assumes es,nothing
        mov     bx,es:[di].ptri_xHotspot
        mov     cx,es:[di].ptri_yHotspot
;/*
;** DX = translated hbmPointer
;** AX = translated hbmColor
;** BX = x hotspot if cursor non null
;** CX = y hotspot if cursor non null
;*/

        assert  ax,NE,0         ; a NULL near ptr means no color bitmap
scc_null_cursor:
        cCall   set_device_cursor,<bx,cx,dx,ax,es:[di].ptri_fPointer,(hddc.lo)>

scc_exit:
        cwd
        cCall   far_leave_driver
        fw_zero <es,cx>
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = set_device_cursor
;*
;* DESCRIPTION   = 
;*
;*    Common routine for DeviceSetCursor and SetColorCursor.
;*   
;*    The given pointer shape is saved in local storage to be used as
;*    the current pointer shape.    If the pointer to the pointer shape
;*    (npbmPointer) is NULL, then no pointer is to be drawn.
;*   
;*    The given pointer shape will have been converted from generic form
;*    into the dimensions requested by this driver.
;*   
;*    Any pointer on the screen will be removed before the new pointer
;*    shape is set.  The drawing of the new pointer will be delayed
;*    until the check_pointer routine is called (check_pointer is the
;*    only routine that can make the pointer become visible).
;*
;*    Registers Preserved:             
;*          DS,BP                      
;*    Registers Destroyed:             
;*          AX,BX,CX,DX,SI,DI,ES,flags 
;*    Calls:                           
;*          pointer_off                
;*          move_pointers              
;*
;* INPUT         = DS = Data 
;* OUTPUT        = None
;*
;* RETURN-NORMAL = AX = 1               
;* RETURN-ERROR  = AX = 0  error logged 
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

cProc   set_device_cursor,<NEAR,PUBLIC>,<ds>
        parmW   sHotX
        parmW   sHotY
        parmW   npbmPointer
        parmW   npbmColor
        parmW   wFlags                  ;pointer type
        parmW   npddc
        localD  pBitsAndXor             ;pointer to and/xor mask bits
        localD  pBitsColor              ;pointer to color mask bits
        localW  tmpcxPointer
        localW  tmpcyPointer
cBegin
        cld

;/*
;**  Validate the incoming pointer shape.  If they passed us a NULL, then
;**  the old cursor is to be removed, and no new cursor defined.  If they
;**  passed anything else, validate it to be a bitmap in the correct
;**  format for us.
;*/

        mov     pBitsAndXor.sel,0       ;Initialize selectors for null bitmaps
        mov     pBitsColor.sel,0

;/*
;**  npbmPointer must be a monochrome bitmap, of the correct width and height.
;**  It is actually a double bitmap containing both the AND and XOR masks.
;**  Therefore the heigth given must be twice the actual image height.
;*/

        mov     si,npbmPointer          ;We have confirmed that it's a bitmap
        or      si,si
        njz     sdc_null_bitmap
        mov     ax,[si].bm_sd.sd_pBits.off
        mov     pBitsAndXor.off,ax
        mov     ax,[si].bm_sd.sd_pBits.sel
        mov     pBitsAndXor.sel,ax

        mov     ax,PMERR_INV_CURSOR_BITMAP
        test    [si].bm_sd.sd_fb,SD_COLOR
        jnz     sdc_log_error

;/*
;**  The driver may allow more than one pointer size.  Check that the
;**  given bitmap has allowed dimensions.
;*/

        mov     bx,[si].bm_sd.sd_cx
        mov     dx,[si].bm_sd.sd_cy     ; Should be double-height because it
        shr     dx,1                    ; has both the AND and XOR masks.
        jc      short sdc_log_error
        mov     si,[DataOFFSET cPtrSizes][2]
        mov     cx,[si][-2]                  ; CX = count of valid ptr sizes
        assert  cx,NE,0                      ; SI -> list of valid ptr sizes

ptr_size_loop:
        cmp     bx,[si]              ; valid cxPtr?
        jne     wrong_cx
        cmp     dx,[si+2]
        je      bitmap_size_is_valid
wrong_cx:
        add     si,4
        loop    ptr_size_loop
        jmp     short sdc_log_error

bitmap_size_is_valid:                ; BX:DX = validated pointer width:height
        mov     tmpcxPointer,bx      ; We don't dare change the actual pointer
        mov     tmpcyPointer,dx      ; size until we've called pointer_off

;/*
;**   Validate npbmColor only if npbmPointer is not null.
;**   npbmColor must be a color bitmap, of the correct width and height.
;*/

        mov     di,npbmColor
        or      di,di
        jz      sdc_looking_good
        mov     si,[di].bm_sd.sd_pBits.off
        mov     pBitsColor.off,si
        mov     si,[di].bm_sd.sd_pBits.sel
        mov     pBitsColor.sel,si

        assert  ax,E,PMERR_INV_CURSOR_BITMAP
        test    [di].bm_sd.sd_fb,SD_COLOR
        jz      sdc_log_error
        cmp     [di].bm_sd.sd_cx,bx
        jne     sdc_log_error
        cmp     [di].bm_sd.sd_cy,dx
        je      sdc_looking_good

sdc_log_error:
        save_error_code
sdc_exit_error:
        xor     ax,ax
        jmp     sdc_exit

sdc_looking_good:
        dec     dx                   ; Map the Y into our hardware
        sub     dx,sHotY             ; coordinate system.  (invert it)
        mov     sHotY,dx

;/*
;** All the bitmap stuff looks real good.  Take down the old pointer and
;** then see about setting this as the new one.
;*/

sdc_null_bitmap:
        mov     ds,PtrCodeData
        assumes ds,PtrData

;/*
;**  $003 --- Begin Insertion ---
;*/

ifdef SLITE
@@:
        test    [fStarlight],00000100b  ;is the Blt engine being programmed? $027
        jnz     @B                      ;yes--don't mess with it           $027
        test    [fStarlight],00000010b  ;is the Blt engine on?
        jz      scd_slite_off           ;no--never mind
        mov     dx,SL_BCommand1
        in      al,dx                   ;wait until the Blt engine
        test    al,BC1_SS               ;is done
        jnz     @B
        test    [fStarlight],00000010b  ;is the Blt engine still on?
        jz      scd_slite_off           ;no--pointer must have turned it off
        call    starlight_disable       ;yes--disable starlight
scd_slite_off:
endif

;/*
;**   $003 --- End Insertion ---
;*/

        xor     cx,cx                   ;Set screen busy
        xchg    screen_busy,cl          ;  and get current status
        ;jcxz   sdc_exit_error          ;Screen is busy, error, ignore call
        assert  cx,NE,0                 ;Walt says it should never happen
        .errnz  IS_BUSY

;/*
;**  Remove any old pointer.
;** 
;**  real_x and real_y are kept as the upper left corner of where the pointer
;**  is to be drawn.  Since the hot spot might change with the pointer shape,
;**  the hot spot must be added back into these locations.  After the new
;**  pointer has been set, the hot spot can be subtracted out for it.
;*/

public  sdc_turn_off_cursor
sdc_turn_off_cursor:
        mov     cl,ptr_flags            ;  Need old value for later
        or      cl,cl                   ; 
        js      sdc_cursor_is_off
        .errnz  PTR_OFF-80h
        cli                             ;Don't want real_x, real_y changing
        xor     ax,ax                   ;Zero old hot spot and add it back
        xchg    ax,hot_x                ;  to real [X,Y] coordinate
        add     real_x,ax
        xor     ax,ax
        xchg    ax,hot_y
        add     real_y,ax
        sti                             ;Allow interrupts

        test    wPrevFlags,PTRI_ANIMATE ;Only skip if last pointer also was
        jz      sdc_remove_cursor       ;  animated (full sized)
        test    wFlags,PTRI_ANIMATE     ;If animating don't clear old cursor
        jnz     sdc_cursor_is_off
sdc_remove_cursor:
        call    pointer_off             ;Remove old pointer from screen
        mov     ptr_flags,PTR_OFF

sdc_cursor_is_off:
        mov     ax,wFlags
        mov     wPrevFlags,ax
        cmp     pBitsAndXor.sel,0       ;pBitsAndXor.sel was set to zero
        jz      sdc_good_exit           ;  if a null bitmap was passed in

;/*
;**   move_pointers saves the new ptr (AND/XOR masks and COLOR bm) in
;**   off-screen memory (on most devices).  If any edges of the new pointer
;**   are transparent then move_pointers may simply reduce the size of the
;**   stored bitmap and associated exclusion rect.
;*/

        mov     bx,tmpcxPointer         ;not used by CGA/EGA/VGA
        mov     dx,tmpcyPointer
        cCall   move_pointers,<pBitsAndXor,pBitsColor,wFlags>

        mov     ds,PtrCodeData
        assumes ds,PtrData

        or      ax,ax                   ;cx=cy=0 iff xparent pointer given
        jz      sdc_good_exit
        mov     cxPointer,ax            ;Pointer width in bits
        mov     cyPointer,dx            ;Pointer height in scans

;/*
;**   Since the pointer must be drawn again (it wasn't a null pointer), we
;**   have to draw it.  To do this, the pointer excluded flag will be set in
;**   ptr_flags.  Only the timer code can bring the pointer back once it has
;**   been excluded, and will do it when the next timer interrupt occurs.
;**   This minimizes the code that has to be written or duplicated in
;**   SetCursor!
;**   Also set up the new hot spot adjustments and adjust the pointer
;**   coordinate for it.
;*/

        cli                             ;Don't want real_x, real_y changing
        mov     ax,sHotX                ;Set X hot spot adjustment
        mov     hot_x,ax
        sub     real_x,ax               ;Adjust real_x for hotspot
        mov     ax,sHotY                ;Ditto for Y
        mov     hot_y,ax
        sub     real_y,ax

        mov     ptr_flags,PTR_EXCLUDED  ;Show excluded, but not hidden
        test    wFlags,PTRI_ANIMATE
        jz      sdc_good_exit
        mov     ptr_flags,PTR_REDRAW    ;Cursor is animated type it needs
                                        ;  to be redrawn
        sti
        mov     screen_busy,NOT_BUSY
        cCall   MoveCursor,<8000h,8000h>

;/*
;**      Whatever had to be done has been.  Restore the interrupt state
;**      to whatever was set by the user and free the screen and make the
;**      screen available.
;*/

sdc_good_exit:
        mov        ax,1
        cli                                ;macro fudge
        sti
        mov     screen_busy,NOT_BUSY

sdc_exit:
        fw_zero <es,cx>
cEnd

;/*
;**   $003 --- Begin Insertion ---
;*/

ifdef SLITE
;/***************************************************************************
;*
;* FUNCTION NAME = starlight_disable
;*
;* DESCRIPTION   = Disables Starlight VGA Bit Blt Engine registers.
;*                 Registers Preserved: AX
;*
;* INPUT         = Interrupts off 
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;* History:
;*
;* PTR/DCR    Date    Who         Remarks
;* -------  --------  ----------- --------------------------------------
;*  24668   01/27/93  T Kirschner Register AH corruption fix
;*
;**************************************************************************/

        assumes ds,PtrData
        assumes es,nothing

        public          starlight_disable
starlight_disable        proc        near

        push    ax

        mov     dx,EGA_BASE+GRAF_ADDR
        mov     ax,GRAF_ESR+100h*ESR_UNLOCK     ;unlock Starlight
        out     dx,ax

        mov     al,GRAF_BBCR                    ;disable Starlight
        out     dx,al
        inc     dx
        in      al,dx
        and     al,not BBCR_BBEN
        mov     ah,al
        or      al,BBCR_RBLT                    ;reset the Blt engine
        out        dx,al

        push    ax                              ;Prevent AH clobber @24668

        DevIODelay                              ;I/O delay destroys ax

        pop     ax                              ;Restore            @24668
  

        in      al,84h                          ;delay for 486
        mov     al,ah
        out     dx,al                           ;turn off the reset
        dec     dx

        mov     ax,GRAF_ESR+100h*ESR_LOCK       ;lock Starlight
        out     dx,ax
        and     [fStarlight],11111101b          ;show Blt engine disabled

        mov     dx,EGA_BASE + SEQ_DATA          ;put VGA in a known state
        mov     al,MM_ALL                       ;taken from EXIT.BLT
        out     dx,al
        mov     dl,GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax
        mov     ax,DR_SET shl 8 + GRAF_DATA_ROT
        out16   dx,ax
        mov     ax,GRAF_ENAB_SR
        out16   dx,ax
        mov     ax,M_DATA_READ shl 8 + GRAF_MODE
        out16   dx,ax
        pop        ax

        ret
starlight_disable       endp

;/***************************************************************************
;*
;* FUNCTION NAME = starlight_enable
;*
;* DESCRIPTION   = Enables Starlight VGA Bit Blt Engine registers.
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;* 
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public          starlight_enable
starlight_enable        proc    far

        push    ds
        mov     ds,[PtrCodeData]
        assumes ds,PtrData

se_1:
        test    [fStarlight],00000010b  ;is the Blt engine on?
        jz      se_2                    ;no--exclude took care of it
        mov     dx,SL_BCommand1         ;yes--wait for it to finish
        in      al,dx
        test    al,BC1_SS
        jnz     se_1
se_2:
        cli                             ;keep pointer from interrupting

        or      [fStarlight],00000110b          ;show Blt engine enabled and
                                                ;being programmed          $027
        mov     dx,EGA_BASE+GRAF_ADDR
        mov     ax,GRAF_ESR+100h*ESR_UNLOCK     ;unlock Starlight
        out     dx,ax

        mov     al,GRAF_BBCR                    ;enable Starlight
        out     dx,al
        inc     dx
        in      al,dx
        or      al,BBCR_BBEN
        out     dx,al
        dec     dx

        mov     ax,GRAF_ESR+100h*ESR_LOCK       ;lock Starlight
        out     dx,ax
        sti                                     ;enable ints               $027
        pop     ds
        assumes ds,nothing

        ret
starlight_enable        endp

;/***************************************************************************
;*
;* FUNCTION NAME = starlight_running
;*
;* DESCRIPTION   = Clears the Starlight/Stardust "being programmed" bit 
;*
;* INPUT         = None
;* OUTPUT        = None
;*
;* RETURN-NORMAL = None
;* RETURN-ERROR  = None
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public          starlight_running
starlight_running       proc    far

        push    ds
        mov     ds,[PtrCodeData]
        assumes ds,PtrData

        and     [fStarlight],11111011b          ;show Blt engine no longer
                                                ;being programmed
        pop     ds
        assumes ds,nothing

        ret
starlight_running       endp

        assumes ds,PtrData
        assumes es,nothing
        public          far_slite_disable
far_slite_disable       proc    far
        call            starlight_disable
        ret
far_slite_disable       endp

endif

;/*
;**  $003 --- End Insertion ---
;*/

sEnd    PtrCode

end
