;*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.;
;*****************************************************************************/
;***********************************************************************
;
;   Module          = EDDCURSR
;
;   Description     = handles cursor drawing via the AI for monochrome
;                     and by direct hardware access for colour
;
;   Function        = MoveCursor
;
;
;***********************************************************************

INCL_PM    EQU     1
include os2.inc
include eddinclt.inc
include eddhcone.inc
include eddhmacr.inc
include eddhtype.inc

include cursor.inc

ifdef _8514
include 8514.inc
endif ;_8514

?DF     equ     1       ; we dont want _TEXT segment defined
include cmacros.inc

;**********************************************************************/
;* Declare external variables                                         */
;**********************************************************************/
; These externs are still ok because they are not used at interrupt time

ifdef MATROX
        extrn   _VRAMBitmap             :BYTE
endif
ifndef _8514
        extrn   _SpriteShapeRoutine     :BYTE
endif ;~_8514

;*************************************
; copied in from exequ.inc
;*************************************
ifdef XGA
sprite_hwvis            equ     0100h+sprite_control    ; sprite visible
sprite_hwinvis          equ     0000h+sprite_control    ; sprite invisible
sprite_transparent      equ     0AAh                    ; transparent byte
endif

ifdef S3
sprite_hwinvis          equ     0h
sprite_hwvis            equ     1h
sprite_16_hwvis         equ     5h
sprite_24_hwvis         equ     9h

status_1                equ     3dah
cur_mode                equ     45h
cur_org_x_hi            equ     46h
cur_org_x_lo            equ     47h
cur_org_y_hi            equ     48h
cur_org_y_lo            equ     49h
cur_fg_stack            equ     4ah
cur_bg_stack            equ     4bh
cur_storage_start_lo    equ     4dh
cur_storage_start_hi    equ     4ch
cur_offset_x            equ     4eh
cur_offset_y            equ     4fh

; * * * * * * * * * * * * * * * *
; Macro for handshaking S3 chips.
; will out to index port and
; data port in two seperate outs.
; * * * * * * * * * * * * * * * *
outb  macro
       mov      dx, S3_NDX_PORT
       out      dx,al
       inc      dx
       ror      ax,8
       out      dx,al
endm

check_vert_retrace  macro
local wait_0
local wait_1
        mov      dx, status_1
wait_0: in       al, dx
        and      al, 8   ; bit 3
        jnz      wait_0
wait_1: in       al, dx
        and      al, 8   ; bit 3
        jz       wait_1
endm


endif

;*************************************
; end of copying
;*************************************

_DATA           segment dword use32 public 'DATA'

        public  _start_of_cursor_data
_start_of_cursor_data    equ this byte

        public  _cursor_data
_cursor_data    CURSORDATA <>

        public  _end_of_cursor_data
_end_of_cursor_data    equ this byte

        public  movecursorblock
movecursorblock equ this byte
        dd      OFFSET FLAT:_start_of_cursor_code
        dd      _end_of_cursor_code - _start_of_cursor_code
        dd      OFFSET FLAT:_start_of_cursor_data
        dd      _end_of_cursor_data - _start_of_cursor_data
_DATA           ends


_CURSOR         segment dword use32 public 'CODE'
                assume  cs:FLAT, ds:FLAT, es:FLAT

public  _start_of_cursor_code
_start_of_cursor_code:

;***********************************************************************
; This is the real start of the 32 bit move cursor routine
; The interface is:
;       The stack holds 3 dword parameters
;               The x and y coordinates
;               and a pointer to locked global memory containing the
;                       cursor data
;
; The internal entry point is used when calling from within the driver.
; The real entry point is the one which will be called at interrupt time.
;***********************************************************************

MoveCursor32:
        .erre   $ eq _start_of_cursor_code
        push    ebp
        mov     ebp,esp
        push    esi
        push    edi
        push    ebx

ifdef   S3
        ; check if the hardware is available at all @DMS
        ; if it is busy , leave and attempt to let the operation finish
        mov     dx,9ae8h
        in      al,dx
        and     al,200
        jnz     move_cursor_end
endif

        ; Get new cursor position off the stack
        mov     ecx, dword ptr [ebp+8]  ; Read new x coordinate
        mov     edx, dword ptr [ebp+12] ; Read new y coordinate

        ; set up access to our data via edi
        mov     edi, dword ptr [ebp+16] ; Read pointer to locked global memory

        ; If both coordinates are 8000H then this is CheckCursor

        cmp     ecx,edx                 ; Are coords equal ?
        jne     short not_check_cursor  ; No - not CheckCursor
        cmp     ecx,8000H               ; Are coords 8000H ?
        jnz     short not_check_cursor  ; No - not CheckCursor

        ; we have a CheckCursor call


        ; if the cursor needs redrawing ...
        cli                          ; Disable interrupts
        test    [edi].cd_cursor_status, CURSOR_REDRAW
        jz      move_cursor_end

        ; copy the current coordinates to ecx, edx
        mov     ecx, [edi].cd_new_cursor_x
        mov     edx, [edi].cd_new_cursor_y
        jmp     short check_cursor_draw

not_check_cursor:
        cli                          ; Disable interrupts

        ; Store new coordinates
        mov     [edi].cd_new_cursor_x,ecx
        mov     [edi].cd_new_cursor_y,edx

        ; since this was a MoveCursor call and not a CheckCursor
        ; one, flag the fact that the cursor has moved since it
        ; was last drawn - this will be reset if we do actually
        ; draw the cursor
        or      [edi].cd_cursor_status, CURSOR_MOVED

check_cursor_draw:
        ; exit if a drawing order is in progress
        ; or we are are already processing the cursor
        ; or the cursor is subject to exclusion anyway
        test    [edi].cd_cursor_status, CURSOR_HWINUSE or          \
                           CURSOR_EXCLUDED or CURSOR_BUSY
        jnz     short move_cursor_end

        ; Make status busy
        or      [edi].cd_cursor_status, CURSOR_BUSY

        ; target for looping back to if the cursor moves
        ; whilst we are drawing it
display_cursor:

        ; if the cursor is not visible then exit
        test    [edi].cd_cursor_status, CURSOR_VISIBLE
        jz      short exit_cursor

        ; we are actually drawing the cursor so reset the flag
        ; saying it moved since it was last drawn
        ; and the one saying we need to redraw it
        and     [edi].cd_cursor_status, NOT (CURSOR_MOVED or CURSOR_REDRAW)

        ; copy the position to eax and ebx, preserving ecx,edx for later
        mov     eax, ecx
        mov     ebx, edx

        ; test if we're currently using a software cursor (either by
        ; choice or because the cursor is colour)
        ; or whether we can use the hw sprite as our cursor

        test    [edi].cd_cursor_status, CURSOR_COLOUR or CURSOR_SOFTWARE
        sti                            ; Enable interrupts
        jz      short hw_cursor

        test    [edi].cd_cursor_status, CURSOR_XREGION
        jz      short sw_cursor
        cCall   CheckXRegion
        jc      short move_complete

sw_cursor:
        cCall   software_cursor

        jmp     short move_complete

hw_cursor:
ifdef XGA

        ; Put the new cursor position in the SPRITE parameter
        ; block and call DrawSprite

        mov     [edi].cd_draw_sprite_x, eax      ; x - coord
        add     ebx, [edi].cd_cursor_adjust      ; (see DeviceSetCursor)
        mov     [edi].cd_draw_sprite_y, ebx      ; y - coord

        cCall   DrawSprite              ; draw the cursor

endif
ifdef S3

        ; Put the new cursor position in the SPRITE parameter
        ; block and call DrawSprite

        mov     [edi].cd_draw_sprite_x, eax      ; x - coord
        add     ebx, [edi].cd_cursor_adjust      ; (see DeviceSetCursor)
        mov     [edi].cd_draw_sprite_y, ebx      ; y - coord

        cCall   DrawSprite              ; draw the cursor

endif

move_complete:
        cli                             ; Disable interrupts

        ; now we have drawn the new cursor we can
        ; save the position in the global
        ; cursor position variables used by eddm_ExcludeCursor
        mov     [edi].cd_cursor_x,ecx
        mov     [edi].cd_cursor_y,edx

        ; Now check whether the cursor position changed
        ; while we were drawing it. If it has changed, we
        ; just loop back and draw it again.
        ; cx, dx contain the just drawn cursor x,y

        cmp     ecx, [edi].cd_new_cursor_x        ; cmp latest x coord
        jne     short position_changed         ; If different then loop
        cmp     edx, [edi].cd_new_cursor_y        ; cmp latest y coord
        je      short exit_cursor              ; If different then loop

position_changed:
        ; get the new cursor position in ecx, edx and go back and do
        ; it all again
        mov     ecx, [edi].cd_new_cursor_x
        mov     edx, [edi].cd_new_cursor_y
        jmp     display_cursor

        ; Position is unchanged so we can clear the busy flag and leave
exit_cursor:
        and     [edi].cd_cursor_status, NOT CURSOR_BUSY

move_cursor_end:

ifdef  S3
        ; Restore Lock State
        ; mov     al, 39h
        ; mov     ah, [edi].cd_sys_ext_lock_reg
        ; outb
endif

        sti                     ; Enable interrupts

check_cursor_exit:
        pop     ebx             ; Restore registers...
        pop     edi
        pop     esi
        pop     ebp

        ret                     ; C calling convention


;**********************************************************************/
; software_cursor
;
;  Inputs:  eax - x coord of cursor hotspot
;           ebx - y coord of cursor hotspot
;           edi - points to cursor data
;
;  Destroys:  eax, ebx, esi
;  Preserves: ecx,edx
;
;  Since there is no hardware sprite support for colour cursors we need to
;  emulate here. The cursor definition (AND / XOR masks and bitmap) are
;  stored in VRAM. The cursor is displayed by inverting all screen bits
;  where the XOR mask is one and bltting the colour bitmap to the screen
;  using the AND mask as a pattern with FgMix leavalone, BgMix source,
;  ie. copy the colour bitmap wherever the AND mask is 0.
;  This gives the correct effect, ie.
;   XOR    AND    COLOR    RESULT
;    1      1       x      Invert Screen
;    0      0       x      x
;    0      1       x      Transparent
;    1      0       x      x
;
;**********************************************************************/

        align   4
cProc   software_cursor,<PUBLIC>,<edx,ecx>
cBegin

ifdef S3
        ;DCR96 - check if we are in linear addressing mode.
        test    [edi].cd_cursor_status, CURSOR_DRVR_IN_LA
        jz      short @F

        push    eax
        push    edx

        ;Take driver out of LA mode
        mov     ax,S3_LAW_CTL
        mov     dx,S3_NDX_PORT
        out     dx,ax
        inc     dx
        in      ax,dx
        and     ax,NOT S3_LINEAR_ADDRESSING
        out     dx,ax

        pop     edx
        pop     eax
@@:
endif

ifdef   _8514
ifdef   SEAMLESS
;/* JMW */
        push    ax
        push    dx
        mov     dx,9ae8h
        in      ax,dx
        test    ax,1000000000b
        pop     dx
        pop     ax
        jz      short queue_not_busy
        jmp     short queue_busy
queue_not_busy:
;/* JMW */
endif
endif

ifndef _8514
        pushxga
endif ;~_8514
        ; eax, ebx hold the cursor hot-spot - we need to adjust them
        ; to get the cursor 'cell' bottom left
        add     ebx, [edi].cd_hotspot_y
        sub     eax, [edi].cd_hotspot_x

; Now we pack the x and y values into a single 32 bit value in ebx
        sub     bx, [edi].cd_software_cursor.sc_hwusedsize.y
        shl     ebx, 10h
        mov     bx, ax

        ; First we need to remove the old cursor (if software)
        ; and restore the screen. If the software cursor definition
        ; x coordinate is 8000h then it indicates no software cursor
        ; displayed so just display the new one (h/w cursor will have
        ; been removed by SetColorCursor). Any other value for the x
        ; coordinate indicates that a software cursor is displayed at
        ; the specified position

        cCall   internal_remove_software_cursor

ifndef _8514
        movxga  [edi].cd_memory_registers
        waitshortcursor
endif ;~_8514

        ; old cursor removed so now draw the new one
        ; first save the screen behind the new cursor position
        ; map A is still set up as the screen which is the source
        ; for this operation: the pixel operation will take care of
        ; this and it mean we don't have to reset the registers
        ; The destination is the colour cursor definition screen
        ; buffer: this is already set up in map B

        ; saving and restoring always BLT the whole cursor size
        ; (rather than just the used part - because the used
        ; part may change between the save and the restore)

        ; source copy mix already selected

        ; the used size contains both x and y dimensions in a single dword !
        mov     eax, dword ptr [edi].cd_software_cursor.sc_hwusedsize
        mov     [edi].cd_savedusedsize,eax

        ; set the blt start coordinates
        ; new cursor position is in ebx
        mov     eax, ebx
        and     bx,bx                   ; check just x coord
        jns     short x_positive

        ; x coordinate is negative - we only want to save that
        ; data actually on the screen so reset the x coordinate
        xor     ax,ax

ifdef OMIT
        ; this bit was supposed to adjust the saved size to just the minimum
        ; required, but it actually increased the amount saved, not decreased
        ; it !!!
        ; Thus ommitting the code saves more than needed, but less than it
        ; did before !
        add             ax, software_cursor.sc_hwusedsize.x

        and             ax, ax          ; check x coord again
        jns             short now_positive
        sub             ax, ax          ; affect just x coord

now_positive:
        mov             [edi].cd_savedusedsize.pts_x, ax
        sub             ax, ax          ; affect just x coord
endif ; OMIT

x_positive:
        mov             [edi].cd_savedcoords, eax

        ; save the new cursor position in the colour cursor definition
        mov     dword ptr [edi].cd_software_cursor.sc_coord, ebx

ifdef _8514
        cCall   draw_pointer

else ;~_8514

; Now see if we have a Matrox or an XGA software cursor to draw
ifdef MATROX
        cmp     [edi].cd_adapter_id, MATROX_ID
        jne     short draw_xga_cursor

        ; We need to draw a software cursor on the MATROX board

        ; get the pointer to the VRAM - we have to be careful to use
        ; either the global or the process valid value
        mov     esi, [edi].cd_screen_address
        mov     eax,ds
        cmp     ax, SEG FLAT:_cursor_data
        jne     short @f
        mov     esi, _VRAMBitmap.virt_addr
@@:

        ; get width in ax, height in eax.hi
        mov     ebx, dword ptr [edi].cd_screen_hw_size
        movzx   eax, bx         ; eax = screen width (zero based)
        inc     eax             ; convert last pel number to width

        ; get the cursor position in edx, ecx
        mov     ecx, [edi].cd_savedcoords
        movzx   edx, cx         ; edx = x coord
        shr     ecx, 16         ; ecx = y coord

        shl     edx,1           ; change pels to bytes (16bpp) - x coord
        shl     eax,1           ; change pels to bytes (16bpp) - screen width
        mov     ebx, eax        ; save bytes per line in ebx

        ; add the x coord offset to the screen start
        add     esi, edx

        ; multiply the screen width (bytes per line) by the y coord
        ; (affects edx)
        mul     ecx

        ; add this to the x coord and screen start
        add     esi, eax
        mov     edx,ebx

        ; quick summary
        ; edx - bytes per scan line
        ; esi - start address of screen rectangle to affect
        ; edi - address of cursor_data

        movzx   ecx, [edi].cd_savedusedsize.pts_y
        inc     ecx                             ; height of cursor

        xor     ebx,ebx                         ; start at start of buffer
draw_each_line:
        push    ecx                             ; save line counter
        push    esi                             ; save start pel pointer

        movzx   ecx, [edi].cd_savedusedsize.pts_x
        inc     ecx                             ; width of cursor
draw_each_pel:
        mov     ax, word ptr [esi]      ; get the current data

        mov     word ptr [edi].cd_cursor_screen[ebx], ax   ; save it
        mov     ax, word ptr [edi].cd_cursor_bm[ebx]
        mov     word ptr [esi], ax                      ; write the data

        add     ebx,2
        add     esi,2
        loop    draw_each_pel

        ; now adjust the buffer pointer because we are only using part
        ; of each buffer scanline
        movzx   eax, [edi].cd_software_cursor.sc_hwsize.pts_x
        sub     ax, [edi].cd_savedusedsize.pts_x
        add     eax, eax                ; change pels to bytes
        add     ebx, eax                ; adjust the buffer pointer

        pop     esi
        add     esi,edx                 ; move to next line
        pop     ecx
        loop    draw_each_line

        jmp    sw_cursor_exit
endif; MATROX

draw_xga_cursor:
        ; old cursor removed so now draw the new one
        ; first save the screen behind the new cursor position

        ; map A is set up as the screen which is the source
        ; for this operation: the pixel operation will take care of
        ; this and it mean we don't have to reset the registers
        ; The destination is the colour cursor definition screen
        ; buffer: this is already set up in map B

        ; select the destination map
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_A

        ; write screen physical address to hardware mapA base
        mov             eax, [edi].cd_screen_address
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapA width and height
        mov             eax, dword ptr [edi].cd_screen_hw_size
        memregwrite     hw_pi_map_size, eax

        ; write mapA format from screen bitmap header
        mov             al, byte ptr [edi].cd_screen_hw_format
        memregwrite     hw_pi_map_format, al

        ; set up the destination - the old screen data beneath the cursor
        ; stored in the software cursor definition screen buffer
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_B

        ; write mapB format - same as the screen (still in al)
        memregwrite     hw_pi_map_format, al

        ; write physical address to hardware mapB base
        mov             eax, [edi].cd_software_cursor.sc_screen
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapB width and height
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwsize
        memregwrite     hw_pi_map_size, eax

        ; select source copy mix
        memregwrite     fg_mix, HWMIX_SOURCE
        memregwrite     bg_mix, HWMIX_SOURCE
        memregwrite     colour_comp_fun, COLCOMP_ALWAYS

        mov             eax, [edi].cd_savedcoords
        memregwrite     src_map, eax
        memregwrite     patt_map, 0
        memregwrite     dest_map, 0

        ; write to both x and y dimensions at once
        mov     eax, [edi].cd_savedusedsize
        memregwrite     dimensions, eax

        ; get the pixelop and kick off the blt to save the screen
        ; under the new cursor
        ; -  background source: source pixel map
        ; -  foreground source: source pixel map
        ; -  step: PxBlt
        ; -  source pixel map: map A
        ; -  destination pixel map: map B
        ; -  pattern pixel map: don't care
        ; -  mask pixel map: boundary disabled
        ; -  drawing mode: don't care
        ; -  direction octant: left to right, top to bottom
        memregwrite     pixel_op, 0a8128000h

        ; take care of the screen pixels to be inverted by
        ; using the XOR mask to invert all screen positions
        ; corresponding to a 1 in the mask (0 -> leavalone)
        waitshortcursor

        ; the destination pixel map set up is still valid so
        ; set up the source - in this case a pattern - using map C
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_C

        ; write physical address to hardware mapC base
        ; the XOR mask follows the AND mask which is pointed to by
        ; the colour cursor definition
        mov             eax, [edi].cd_software_cursor.sc_xormask
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapC width and height
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwsize
        memregwrite     hw_pi_map_size, eax

        ; write mapC format - always 1 bit per pixel motorola
        memregwrite     hw_pi_map_format, MOTOROLA+ONE_BPP

        ; select foreground mix destination invert
        ; select background mix destination
        memregwrite     fg_mix, HWMIX_NOTDEST
        memregwrite     bg_mix, HWMIX_DEST

        ; set the blt dimensions
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwusedsize
        memregwrite     dimensions, eax

        ; set the blt start coordinates - the cursor position is held
        ; in ebx
        memregwrite     src_map, 0
        memregwrite     patt_map, 0
        memregwrite     dest_map, ebx

        ; get the pixelop and kick off the blt to invert screen
        ; pixels corresponding to a 1 in the XOR mask
        ; -  background source: don't care
        ; -  foreground source: don't care
        ; -  step: PxBlt
        ; -  source pixel map: don't care
        ; -  destination pixel map: map A
        ; -  pattern pixel map: map C
        ; -  mask pixel map: boundary disabled
        ; -  drawing mode: don't care
        ; -  direction octant: left to right, top to bottom
        memregwrite     pixel_op, 008113000h

        ; finally...
        ; copy the colour bitmap defining the cursor using the
        ; AND mask to control the operation - copy the bitmap when
        ; the AND mask is 0, do nothing when it is 1
        waitshortcursor

        ; the destination pixel map set up is still valid so
        ; set up the source - the colour bitmap - using map B
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_B

        ; write physical address to hardware mapB base
        ; this is stored in the colour cursor definition bm field
        mov             eax, [edi].cd_software_cursor.sc_bm
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapB width and height
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwsize
        memregwrite     hw_pi_map_size, eax

        ; write mapB format - always screen format
        mov             al, byte ptr [edi].cd_screen_hw_format
        memregwrite     hw_pi_map_format, al

        ; now the pattern mask (AND mask) using map C
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_C

        ; write physical address to hardware mapC base
        ; the AND mask is pointed to by the masks field of the
        ; colour cursor definition
        mov             eax, [edi].cd_software_cursor.sc_andmask
        memregwrite     hw_pi_map_base_ptr, eax

        ; width and height are unchanged from the last (XOR)
        ; operation, format is also unchanged

        ; select foreground mix destination
        ; select background mix source
        memregwrite     fg_mix, HWMIX_DEST
        memregwrite     bg_mix, HWMIX_SOURCE

        ; set the blt dimensions
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwusedsize
        memregwrite     dimensions, eax

        ; set the blt start coordinates - the cursor position is held
        ; in ebx
        memregwrite     src_map, 0
        memregwrite     patt_map, 0
        memregwrite     dest_map, ebx

        ; get the pixelop and kick off the blt to copy the bitmap
        ; to the screen where the AND mask is 0
        ; -  background source: source pixel map
        ; -  foreground source: don't care
        ; -  step: PxBlt
        ; -  source pixel map: map B
        ; -  destination pixel map: map A
        ; -  pattern pixel map: map C
        ; -  mask pixel map: boundary disabled
        ; -  drawing mode: don't care
        ; -  direction octant: left to right, top to bottom
        memregwrite     pixel_op, 088213000h

sw_cursor_exit:
        popxga

endif ;~_8514
queue_busy:

ifdef S3
        ;DCR96 - check if we were in linear addressing mode.
        test    [edi].cd_cursor_status, CURSOR_DRVR_IN_LA
        jz      short @F

        ;Restore Bank locations
        mov     ebx,[edi].cd_bank_values

        mov     ax,S3_CRTR_LOCK
        mov     dx,S3_NDX_PORT
        out     dx,ax
        inc     dx
        in      ax,dx
        and     ax,0fff0h
        and     bx,0fh
        or      ax,bx
        out     dx,ax

        mov     ebx,[edi].cd_bank_values
        mov     ax,S3_EX_SCTL_2
        mov     dx,S3_NDX_PORT
        out     dx,ax
        inc     dx
        in      ax,dx
        and     ax,0ffcfh
        and     bx,30h
        or      ax,bx
        out     dx,ax

        ;Put Driver back in LA mode
        mov     ax,S3_LAW_CTL
        mov     dx,S3_NDX_PORT
        out     dx,ax
        inc     dx
        in      ax,dx
        or      ax,S3_LINEAR_ADDRESSING
        out     dx,ax
@@:

endif
cEnd

; overwrites the software cursor with the saved underlying screen data
; no drawing is done if the cursor x coordinate is 8000h indicating no
; saved data
; the source and destination pixel maps are set up before the test for
; x = 8000h since they are needed by MoveCursor to draw the cursor after
; this call (although not by eddm_DeviceSetCursor which also calls this)

        align   4
cProc   remove_software_cursor,<PUBLIC>,<edi,esi,ebx>

cBegin
; set up the data segment because we currently have 0's in the selectors
        mov     ax, SEG FLAT:_cursor_data
        mov     ds, eax
        mov     es, eax

; ensure we can access the cursor data
        lea     edi, _start_of_cursor_data

        cCall   internal_remove_software_cursor
cEnd

;**********************************************************************/
; CheckXRegion
;
;  Description: This routine checks to see if the new cursor position
;               is in the Exclude region. If so it returns with the
;               carry flag set. If not carry flag will be cleared.
;
;  Inputs: edi = cursor_data
;
;  Destroys:  None
;  Preserves: All
;
;  Returns: Carry flag set - Cursor is in the exclude region
;           Carry flag clear - Cursor is not in the exclude region
;**********************************************************************/
cProc   CheckXRegion,<PUBLIC>,<>

cBegin

        pusha
        mov     eax, [edi].cd_software_cursor.sc_XcludeRect.rcl_xLeft
        mov     ebx, [edi].cd_cursor_x
        sub     ebx, [edi].cd_hotspot_x
        cmp     ebx, eax
        jl      short not_in_region

        mov     eax, [edi].cd_software_cursor.sc_XcludeRect.rcl_xRight
        cmp     ebx, eax
        jg      short not_in_region

        mov     eax, [edi].cd_software_cursor.sc_XcludeRect.rcl_yTop
        mov     ebx, [edi].cd_cursor_y
        add     ebx, [edi].cd_hotspot_y
        inc     ebx
        cmp     ebx, eax
        jl      short not_in_region

        mov     eax, [edi].cd_software_cursor.sc_XcludeRect.rcl_yBottom
        cmp     ebx, eax
        jg      short not_in_region

        test    [edi].cd_cursor_status, CURSOR_VISIBLE
        jz      short no_hide
        cCall   internal_remove_software_cursor

no_hide:
        stc                            ; Cursor in the X region
        jmp     short  cxr_exit

not_in_region:                         ; Cursor is not in X region
        clc

cxr_exit:
        popa

cEnd

;**********************************************************************/
; internal_remove_software_cursor
;
;  Inputs: in cursor_data
;
;  Destroys:  eax, esi, ecx, edx
;  Preserves: ebx, edi
;**********************************************************************/

        align   4
cProc   internal_remove_software_cursor,<PUBLIC>,<ebx>

        localD  adjusted_coords
        localD  adjusted_dimensions

ifdef _8514
        localW save_x_cell
        localW save_y_cell
        localW cxPointer
        localW cyPointer
        localW old_x_cell
        localW old_y_cell
endif ;_8514

cBegin
        ; check if we need to remove the old cursor
        cmp     [edi].cd_software_cursor.sc_coord.pts_x, 8000h
        je      cursor_removed

ifdef _8514

        ;Size of pointer
        mov     ax,[edi].cd_savedusedsize.pts_x
        mov     cxPointer,ax
        mov     ax,[edi].cd_savedusedsize.pts_y
        mov     cyPointer,ax

        ;Position of current pointer
        mov     eax,[edi].cd_savedcoords
        movzx   edx, ax         ; x coord
        shr     eax, 16         ; y coord

        mov     old_y_cell,ax
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     dx,0fffh
        mov     ax,dx
        shl     dx,1
        add     dx,ax
@@:
        endif
        mov     old_x_cell,dx

        ;SAVE area in VRAM
        mov     eax,[edi].cd_software_cursor.sc_screen
        mov     save_y_cell,ax
        ror     eax,16
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     ax,0fffh
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     save_x_cell,ax


; Copy the contents of the save buffer back to the location in the framebuffer
; from which it was taken

        WaitQIdle
        WaitQ   8                       ; need room in the queue
        outwQ   XMIN,(XMIN_2DECODE+0)   ; set up scissor rectangle
        ifndef  S3
        mov     ax,SCR_8514_WIDTH-1
        else
        ;mov     ax,SCR_S3_WIDTH-1
        mov      ax,[edi].cd_screen_hw_size
       ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
       endif
        endif
        or      ah,High XMAX_2DECODE
        outwQ   XMAX,ax
        outwQ   YMIN,(YMIN_2DECODE+0)
        ifndef  S3
        mov     ax,SCR_8514_HEIGHT-1
        else
        mov     ax,SCR_S3_HEIGHT-1
        endif
        or      ah,High YMAX_2DECODE
        outwQ   YMAX,ax
        ifndef  S3
        mov     ax,00ffh                 ; read/write to/from all planes
        else
        mov     ax,0ffffh                ; read/write to/from all planes
        endif
        outwQ   READ_ENABLE,ax
        outwQ   WRITE_ENABLE,ax

        mov     ax,cxPointer
        outwQ   LX,ax                   ; 0-based width of the save area
        mov     ax,cyPointer
        outwQ   LY,ax                   ; 0-based height of a save area

        WaitQIdle

        WaitQ   7
        outwQ   X0,save_x_cell          ; ul of save area
        outwQ   Y0,save_y_cell
        outwQ   X1,old_x_cell           ; send out destination x coordinate
        outwQ   Y1,old_y_cell           ; send out destination y coordinate
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S); straight replace mode
        outwQ   MODE,(MODE_2DECODE+MD_PS_ONES+MD_UP_FALSE)
        outwQ   CMD_FLAGS,CMD_WRESTORE  ; command to restore save area

endif ;_8514

        ; since we will have written the saved data to the screen
        ; it will no longer be valid saved data
        ; mark it as invalid by setting the x_coord to 8000h
        mov     [edi].cd_software_cursor.sc_coord.pts_x, 8000h

ifndef _8514

        ; a cursor is displayed so overwrite it with the saved screen data

; NEW CODE
        ; we hope the adjusted values were calculated when we did the save !
        mov     eax, [edi].cd_savedusedsize
        mov     adjusted_dimensions, eax
        mov     eax, [edi].cd_savedcoords
        mov     adjusted_coords, eax
; end of NEW CODE

        ; get the base address of the hardware registers
        ; this is a GDT selector returned by the ring 0 code
        movxga  [edi].cd_memory_registers

        ; ensure the hardware is not busy then set up the destination
        waitshortcursor

        ; select the destination map
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_A

        ; write screen physical address to hardware mapA base
        mov             eax, [edi].cd_screen_address
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapA width and height
        mov             eax, dword ptr [edi].cd_screen_hw_size
        memregwrite     hw_pi_map_size, eax

        ; write mapA format from screen bitmap header
        mov             al, byte ptr [edi].cd_screen_hw_format
        memregwrite     hw_pi_map_format, al

        ; set up the source - the old screen data beneath the cursor
        ; stored in the software cursor definition screen buffer
        memregwrite     hw_pi_map_index, SEL_PIX_MAP_B

        ; write mapB format - same as the screen (still in al)
        memregwrite     hw_pi_map_format, al

        ; write physical address to hardware mapB base
        mov             eax, [edi].cd_software_cursor.sc_screen
        memregwrite     hw_pi_map_base_ptr, eax

        ; write mapB width and height
        mov             eax, dword ptr [edi].cd_software_cursor.sc_hwsize
        memregwrite     hw_pi_map_size, eax

        ; select source copy mix
        memregwrite     fg_mix, HWMIX_SOURCE
        memregwrite     bg_mix, HWMIX_SOURCE
        memregwrite     colour_comp_fun, COLCOMP_ALWAYS

        ; set the blt start coordinates
        memregwrite     src_map, 0
        memregwrite     patt_map, 0

        ; set the blt dimensions
        mov             eax, adjusted_dimensions
        memregwrite     dimensions, eax

        ; set the blt target coords
        mov             eax, adjusted_coords
        memregwrite     dest_map, eax

        ; get the pixelop and kick off the blt to restore the
        ; screen under the old cursor
        ; -  background source: source pixel map
        ; -  foreground source: source pixel map
        ; -  step: PxBlt
        ; -  source pixel map: map B
        ; -  destination pixel map: Map A
        ; -  pattern pixel map: don't care
        ; -  mask pixel map: boundary disabled
        ; -  drawing mode: don't care
        ; -  direction octant: left to right, top to bottom
        memregwrite     pixel_op, 0a8218000h

endif ;~_8514

cursor_removed:

cEnd

ifdef _8514
;**************************************************************************
;*
;* FUNCTION NAME = draw_pointer
;*
;* DESCRIPTION   = Draw a pointer based at ptsX and ptsY
;*                 1) apply mask1 to screen (may be AND or XOR mask)
;*                 2) apply mask2 to screen (may be AND or XOR mask)
;*                 3) if color copy the color bitmap to screen
;*
;* INPUT         = EDI points to cursor data
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************

cProc   draw_pointer,<PUBLIC>
        localW ptsX
        localW ptsY
        localW save_x_cell
        localW save_y_cell
        localW and_x_cell
        localW and_y_cell
        localW xor_x_cell
        localW xor_y_cell
        localW clrptr_x_cell
        localW clrptr_y_cell
        localW cxPointer
        localW cyPointer
        localW ptsXsave
        localW ptsYsave
cBegin

        ;Pointer size
        mov     ax,[edi].cd_savedusedsize.pts_x
        mov     cxPointer,ax
        mov     bx,[edi].cd_savedusedsize.pts_y
        mov     cyPointer,bx

        ;Current X pointer position
        mov     ax,[edi].cd_software_cursor.sc_coord.pts_x
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     ptsXsave,ax
        mov     ptsX,ax

        ;Check the x coordinate for negative
        or      ax,ax
        jns     short @f                ; x coordinate is in range
        mov     bx,ax
        neg     bx
        sub     [edi].cd_savedusedsize.pts_x,bx
                                        ; swizzle the x coordinate
        and     ax,0000000111111111b    ; isolate low order bits
        or      ah,High XY_NEG          ; add in negative indicator
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short not_24
        mov     dx,ax
        shl     ax,1
        add     ax,dx
not_24:
        endif
        mov     ptsXsave,0
        mov     ptsX,ax
@@:

        ;Current Y pointer position
        mov     bx,[edi].cd_software_cursor.sc_coord.pts_y
        mov     ptsYsave,bx
        mov     ptsY,bx

        ;AND mask in VRAM
        mov     eax,[edi].cd_software_cursor.sc_andmask
        mov     and_y_cell,ax
        ror     eax,16
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     ax,0fffh
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     and_x_cell,ax

        ;XOR mask in VRAM
        mov     eax,[edi].cd_software_cursor.sc_xormask
        mov     xor_y_cell,ax
        ror     eax,16
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     ax,0fffh
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     xor_x_cell,ax

        test    [edi].cd_cursor_status, CURSOR_COLOUR
        jz      short @f
        ;Color bitmap in VRAM
        mov     eax,[edi].cd_software_cursor.sc_bm
        mov     clrptr_y_cell,ax
        ror     eax,16
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     ax,0fffh
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     clrptr_x_cell,ax
@@:
        ;SAVE area in VRAM
        mov     eax,[edi].cd_software_cursor.sc_screen
        mov     save_y_cell,ax
        ror     eax,16
        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        and     ax,0fffh
        mov     dx,ax
        shl     ax,1
        add     ax,dx
@@:
        endif
        mov     save_x_cell,ax

; Set clipping to the max we'll need here
        ifndef  S3
        setclip 0,0,SCR_8514_WIDTH-1,SCR_8514_HEIGHT-1
        else
        setclip 0,0,SCR_S3_WIDTH-1,SCR_S3_HEIGHT-1
        endif
        setlimits cxPointer, cyPointer

;-----------------------------------------------------------------------;
; copy current pointer position to save area
;-----------------------------------------------------------------------;

        copyrect    save_x_cell,save_y_cell, ptsXsave, ptsYsave

; Set clipping to the max we'll need here
        mov     ebx,[edi].cd_screen_hw_size
        mov     ecx,ebx                ; width is in cx

        ifdef   BPP24
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short @f
        mov     ax,cx
        shl     cx,1
        add     cx,ax
@@:
        endif

        shr     ebx,16                 ;Get the height of visible screen.
        ifndef  S3
        setclip 0,0,SCR_8514_WIDTH-1,bx
        else
        setclip 0,0,cx,bx
        endif

;-----------------------------------------------------------------------;
; apply the AND mask to the rectangle on the screen that will be
; receiving the pointer image
;
; Note:  For color pointers we do the XOR mask first, then the AND mask.
;-----------------------------------------------------------------------;
public  mask1_to_screen
mask1_to_screen:

        WaitQIdle
        WaitQ   1                       ; need room in the queue
        outwQ   READ_ENABLE,READ_PLANE_0 ; read from just the 0th plane

        WaitQ   8
        test    [edi].cd_cursor_status, CURSOR_COLOUR
        jnz     short dp_color_ptr
        outwQ   X0,and_x_cell           ; ul of AND mask
        outwQ   Y0,and_y_cell
        outwQ   FUNCTION_0,(FUNC_2OP_COPY+FUNC_S_AND_D); AND the mask
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S_AND_D); AND the mask
        ??Qouts = ??Qouts - 4           ; See def of WaitQ and outwQ in 8*.inc
        jmp     short   mono_ptr_handled
dp_color_ptr:
        outwQ   X0,xor_x_cell           ; ul of XOR mask
        outwQ   Y0,xor_y_cell
        outwQ   FUNCTION_0,(FUNC_2OP_COPY+FUNC_S_XOR_D); XOR the mask
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S_XOR_D); XOR the mask
mono_ptr_handled:
        outwQ   X1,ptsX                 ; send out destination x coordinate
        outwQ   Y1,ptsY                 ; send out destination y coordinate
        outwQ   MODE,(MODE_2DECODE+MD_PS_COPY+MD_UP_FALSE)
        outwQ   CMD_FLAGS,CMD_WMASK     ; command to write mask

;-----------------------------------------------------------------------;
; apply the XOR mask to the rectangle on the screen that will be receiving
; the pointer image
;
; Note:  For color pointers we did the XOR mask first, and now we will
;        do the AND mask.
;-----------------------------------------------------------------------;
public  mask2_to_screen
mask2_to_screen:

        WaitQIdle
        WaitQ   8                       ; need room in the queue
        test    [edi].cd_cursor_status, CURSOR_COLOUR
        jnz     short dp_color_pointer
        outwQ   X0,xor_x_cell           ; ul of XOR mask
        outwQ   Y0,xor_y_cell
        outwQ   FUNCTION_0,(FUNC_2OP_COPY+FUNC_S_XOR_D); XOR the mask
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S_XOR_D); XOR the mask
        ??Qouts = ??Qouts - 4           ; See def of WaitQ and outwQ in 8*.inc
        jmp     short   mono_pointer_handled
dp_color_pointer:
        outwQ   X0,and_x_cell           ; ul of AND mask
        outwQ   Y0,and_y_cell
        outwQ   FUNCTION_0,(FUNC_2OP_COPY+FUNC_S_AND_D); AND the mask
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S_AND_D); AND the mask
mono_pointer_handled:
        outwQ   X1,ptsX                 ; send out destination x coordinate
        outwQ   Y1,ptsY                 ; send out destination y coordinate
        outwQ   MODE,(MODE_2DECODE+MD_PS_COPY+MD_UP_FALSE)
        outwQ   CMD_FLAGS,CMD_WMASK     ; command to write mask

        test    [edi].cd_cursor_status, CURSOR_COLOUR
        jz      short ptr_done

;-----------------------------------------------------------------------;
; Color Pointer
;
; We have a color pointer.  This consists of the two masks (AND and XOR)
; just copied and a color bitmap.  Whereever the AND mask is "1" we draw
; the bitmap.  Wherever the AND mask is "0" we do LEAVEALONE (transparent)
; if the XOR mask is "0"; we do "dest invert" if the XOR mask is "1":
;
;   XOR    AND    Result
;   ---    ---    ------
;    0      0     use color bitmap
;    1      0     use color bitmap
;    0      1     transparent (screen leave alone)
;    1      1     invert      (invert screen)
;
; By doing the XOR before the AND we have now set all the screen bits
; as we want them.  We have "0"s where the bitmap will go.  The bitmap
; image has been pre-processed so that it has "0"s where it will not be
; used.  Thus we can simply "OR" the bitmap onto the screen.
;-----------------------------------------------------------------------;
public color_to_screen
color_to_screen:

        WaitQIdle
        WaitQ   2
        outwQ   READ_ENABLE,READ_ALL_PLANES
        outwQ   WRITE_ENABLE,WRITE_ALL_PLANES

        WaitQ   7                       ; need room in the queue
        outwQ   X0,clrptr_x_cell        ; starting x of color pointer cache
        outwQ   Y0,clrptr_y_cell        ; starting y of color pointer cache
        outwQ   X1,ptsX                 ; send out destination x coordinate
        outwQ   Y1,ptsY                 ; send out destination y coordinate
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S_OR_D)  ; OR the bitmap
        outwQ   MODE,(MODE_2DECODE+MD_PS_ONES+MD_UP_FALSE)
        outwQ   CMD_FLAGS,CMD_MCOLOR    ; command to write it

ptr_done:

cEnd

endif
ifdef XGA

        align   4
cProc   OutputSprite,<PUBLIC>

; Draws the current sprite at the hot spot specified in the draw_sprite data

cBegin
        mov          edx, [edi].cd_io_registers
        add          dx, index

; Subtract the hot spot offset from the hot spot (x coordinate only).
        mov          ebx, [edi].cd_draw_sprite_x
        sub          ebx, [edi].cd_draw_sprite_hot_x

; If the result is negative (off left of screen) then negate it
        jns          short x_pos
        neg          ebx

; Write to hardware sprite preset x
        mov          al, horiz_sprite_preset
        mov          ah, bl
        out          dx, ax

; Set hardware sprite x = 0
        mov          ax, horiz_sprite_start_lo
        out          dx, ax
        mov          al, horiz_sprite_start_hi
        out          dx, ax

        jmp          short do_y

x_pos:
; Hot spot minus hot spot offset is non-negative
; Write to hardware sprite x
        mov          al, horiz_sprite_start_lo
        mov          ah, bl
        out          dx, ax
        mov          al, horiz_sprite_start_hi
        mov          ah, bh
        out          dx, ax

; Set hardware sprite preset x = 0
        mov          ax, horiz_sprite_preset
        out          dx, ax

do_y:
; Subtract the hot spot offset from the hot spot (y coordinate only).
        mov          ebx, [edi].cd_draw_sprite_y
        sub          ebx, [edi].cd_draw_sprite_hot_y

; If the result is negative (off top of screen) then negate it
        jns          short y_pos
        neg          ebx

; Write to hardware sprite preset y
        mov          al, vert_sprite_preset
        mov          ah, bl
        out          dx, ax

; Set hardware sprite y = 0
        mov          ax, vert_sprite_start_lo
        out          dx, ax
        mov          al, vert_sprite_start_hi
        out          dx, ax
        jmp          short sprite_on

y_pos:
; Hot spot minus hot spot offset is non-negative
; Write to hardware sprite y
        mov          al, vert_sprite_start_lo
        mov          ah, bl
        out          dx, ax
        mov          al, vert_sprite_start_hi
        mov          ah, bh
        out          dx, ax

; Set hardware sprite preset y = 0
        mov          ax, vert_sprite_preset
        out          dx, ax

sprite_on:

; Set hardware sprite control to sprite_hwvis
        mov          ax, sprite_hwvis
        out          dx, ax
cEnd

;**********************************************************************/
; DrawSprite
;
;  Inputs:  edi - points to cursor data
;           cursor_data.draw_sprite.x / y
;
;  Destroys: eax, ebx
;
; saves the common palette registers,
; draws the current sprite at the hot spot specified in the draw_sprite data
; and restores the common palette registers.
;
;**********************************************************************/

        align   4
cProc   DrawSprite,<PUBLIC>,<esi,edx>

cBegin
        pushxga

        movxga  [edi].cd_memory_registers
        waitshortcursor

        mov     edx, [edi].cd_io_registers
        add     dx, index

; Save common palette registers
        in      al, dx
        push    eax

        mov     al, sprite_indx_write_lo
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, sprite_indx_write_hi
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_red
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_green
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_blue
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, sprite_save_restore
        out     dx, al
        iowait
        in      ax, dx
        push    eax

; Now actually draw the sprite
        push    edx
        call    OutputSprite
        pop     edx

; Restore common palette registers
        pop     eax
        out     dx, ax

        ;palette_blue
        pop     eax
        out     dx, ax

        ;palette_green
        pop     eax
        out     dx, ax

        ;palette_red
        pop     eax
        out     dx, ax

        ;sprite_indx_write_hi
        pop     eax
        out     dx, ax

        ;sprite_indx_write_lo
        pop     eax
        out     dx, ax

        ;index
        pop     eax
        out     dx, al

        popxga
cEnd


;/**********************************************************************
;/* Function:  SetSpriteShape
;/* Purpose:   Passes the mouse pointer definition to the XGA sprite
;/*
;/*  This routine makes the far call through the callgate which is
;/*   needed in order to run the routine with IOPL (actually at ring 0)
;/**********************************************************************

        align   4
cProc   SetSpriteShape,<PUBLIC,NODATA>,<ecx,edi,esi,ebx>

cBegin
        call    fword ptr _SpriteShapeRoutine.cg_offset
cEnd


; We have to use a Masm proc, not a cMacros proc for this routine in
; order to get exactly what we want

        align   4
        public  _SetSpriteShapeWorker
_SetSpriteShapeWorker   proc far
        pushxga

; set up the data segment because we currently have 0's in the selectors
        mov     ax, SEG FLAT:_cursor_data
        mov     ds, eax
        mov     es, eax

; ensure we can access the cursor data
        lea     edi, _start_of_cursor_data

; get access to the XGA registers, and wait for the h/w to be free
        movxga  [edi].cd_memory_registers
        waitshortcursor

        mov     edx, [edi].cd_io_registers
        add     edx, index

; the easy path is when we simply turn the sprite off
        cmp     [edi].cd_draw_sprite_flags, SPRITE_VISIBLE
        je      short sprite_is_visible

; Set hardware sprite control to sprite_hwinvis to remove the sprite
        mov     eax, sprite_hwinvis
        out     dx, ax
        jmp     sss_exit

sprite_is_visible:
; Save common palette registers
        in      al, dx
        push    eax

        mov     al, sprite_indx_write_lo
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, sprite_indx_write_hi
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_red
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_green
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, palette_blue
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        mov     al, sprite_save_restore
        out     dx, al
        iowait
        in      ax, dx
        push    eax

        cld                                    ; esi increment


; Calculate sprite_byte_width = sprite width in pels / 4
        mov          eax, [edi].cd_draw_sprite_width
        shr          eax, 2
        mov          [edi].cd_sprite_byte_width, eax
        mov          ebx, 16
        sub          ebx, eax             ; get remaining width in ebx

; Set hardware sprite control to sprite_hwinvis
        mov          eax, sprite_hwinvis
        out          dx, ax

; Set hardware sprite write index = 0 (Start writing data at start of sprite)
        mov          eax, sprite_indx_write_lo
        out          dx, ax
        mov          al, sprite_indx_write_hi
        out          dx, ax

; point at the cursor 2bpp definition
        lea          esi, [edi].cd_cursor_bits

; set up io reg for sprite data
        mov          al, sprite_data
        out          dx, al
        inc          dx

; Do until lines written = sprite height
        mov          ecx, [edi].cd_draw_sprite_height

next_line:
; Do until bytes written = sprite_byte_width
        push         ecx
        mov          ecx, [edi].cd_sprite_byte_width
        jecxz        no_sprite_data

@@:
; Get each byte from sprite_addr (esi) and
; Write to hardware sprite data
        lods         byte ptr [esi]
        out          dx, al
        loop         @B

no_sprite_data:

; Do until total bytes written = 16
        mov          ecx, ebx
        jecxz        no_transparent

; Set hardware sprite data = transparent
        mov          al, sprite_transparent

@@:
        out          dx, al
        loop         @B

no_transparent:
; Restore line count
        pop          ecx
        loop         next_line

; Do until total lines written = 64
        mov          ecx, 64
        sub          ecx, [edi].cd_draw_sprite_height
        jecxz        no_trans_lines
        mov          al, sprite_transparent

next_trans_line:
; Do until bytes written = 16
        push         ecx
        mov          ecx, 16

; Set hardware sprite data = transparent
@@:
        out          dx, al
        loop         @B

; Restore line count
        pop          ecx
        loop         next_trans_line

no_trans_lines:
; Write sprite colour data to to hardware
        dec           edx
        mov           ah, [edi].cd_draw_sprite_col1.rgb2_bGreen
        mov           al,sprite_colour_0_green
        out           dx, ax

        mov           ah, [edi].cd_draw_sprite_col1.rgb2_bRed
        mov           al,sprite_colour_0_red
        out           dx, ax

        mov           ah, [edi].cd_draw_sprite_col1.rgb2_bBlue
        mov           al,sprite_colour_0_blue
        out           dx, ax

        mov           ah, [edi].cd_draw_sprite_col2.rgb2_bGreen
        mov           al,sprite_colour_1_green
        out           dx, ax

        mov           ah, [edi].cd_draw_sprite_col2.rgb2_bRed
        mov           al,sprite_colour_1_red
        out           dx, ax

        mov           ah, [edi].cd_draw_sprite_col2.rgb2_bBlue
        mov           al,sprite_colour_1_blue
        out           dx, ax

; Call OutputSprite (draws current sprite at hot pos)
        push         edx
        call         OutputSprite
        pop          edx
; Restore common palette registers

        ;sprite_save_restore
        pop     eax
        out     dx, ax

        ;palette_blue
        pop     eax
        out     dx, ax

        ;palette_green
        pop     eax
        out     dx, ax

        ;palette_red
        pop     eax
        out     dx, ax

        ;sprite_indx_write_hi
        pop     eax
        out     dx, ax

        ;sprite_indx_write_lo
        pop     eax
        out     dx, ax

        ;index
        pop     eax
        out     dx, al

sss_exit:
        popxga

        retf

_SetSpriteShapeWorker   endp

endif  ; XGA

ifdef S3

        align   4
cProc   OutputSprite,<PUBLIC>

; Draws the current sprite at the hot spot specified in the draw_sprite data

cBegin
        ;cli
        ; save index register in case we interrupted a harware op.
        ;xor     eax,eax
        ;mov     dx,3d4h
        ;in      al,dx
        ;push    eax

; Subtract the hot spot offset from the hot spot (x coordinate only).
; edb fix for 73808 - hardware cursor pattern offset should be in pels
; and don't check hw_format 4 times
        mov     ebx, [edi].cd_draw_sprite_x
        mov     eax, [edi].cd_draw_sprite_hot_x
        mov     edx, 0
        sub     ebx, eax
        jns     @F
        neg     ebx                     ; if off screen then use origin 0
        mov     edx, ebx                ; and nonzero cursor offset
        mov     ebx, 0
        jmp     short x_pos
@@:
        cmp     [edi].cd_screen_hw_format, SIXTEEN_BITS_PER_PEL
        jnz     @F
        shl     ebx, 1
@@:
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short x_pos
        mov     eax, ebx
        shl     ebx, 1
        add     ebx, eax

; Write to hardware sprite x
; edx == cursor offset
; ebx == cursor origin
x_pos:
        mov     al, cur_offset_x
        mov     ah, dl
        outb
        mov     al, cur_org_x_lo
        mov     ah, bl
        outb
        mov     al, cur_org_x_hi
        mov     ah, bh
        outb

do_y:
; Subtract the hot spot offset from the hot spot (y coordinate only).
        mov          ebx, [edi].cd_draw_sprite_y
        sub          ebx, [edi].cd_draw_sprite_hot_y

; If the result is negative (off top of screen) then negate it
        jns          short y_pos
        neg          ebx

; Write to hardware sprite preset y
        mov          al, cur_offset_y
        mov          ah, bl
        outb

; Set hardware sprite y = 0
        mov          ax, cur_org_y_lo
        outb
        mov          ax, cur_org_y_hi
        outb
        jmp          short os_end

y_pos:
; Hot spot minus hot spot offset is non-negative
; Write to hardware sprite y
        mov          al, cur_org_y_lo
        mov          ah, bl
        outb
        mov          al, cur_org_y_hi
        mov          ah, bh
        outb

; Set hardware sprite preset y = 0
        mov          ax, cur_offset_y
        outb

os_end:
        ; restore index register in case we interrupted a harware op.
        ;pop     eax
        ;mov     dx,3d4h
        ;out     dx,al
        ;sti

cEnd

        align   4
cProc   SetSprite,<PUBLIC>

; sets up sprite color and turns sprite on

cBegin

; Set hardware sprite control to sprite_hwvis

        cmp          [edi].cd_screen_hw_format, EIGHT_BITS_PER_PEL
        jnz          short @F

        ; think about updating colors here as well!
        ; here we will not use the hw true color stack registers


        ; think about updating colors here as well!
        ; here we will not use the hw true color stack registers

        check_vert_retrace
        mov          al, cur_mode
        mov          ah, sprite_hwvis
        outb
        jmp          set_end
@@:
        ; Reset Color stack for 16 and 24bit modes

        mov          ax, cur_mode
        mov          dx, S3_NDX_PORT
        out          dx, ax
        inc          dx
        in           ax, dx

        cmp          [edi].cd_screen_hw_format, SIXTEEN_BITS_PER_PEL
        jnz          @F

        mov          al, cur_bg_stack
        mov          ah, [edi].cd_draw_sprite_col1.s3clr_clr8
        outb
        mov          al, cur_bg_stack
        mov          ah, [edi].cd_draw_sprite_col1.s3clr_clr16
        outb

        mov          al, cur_fg_stack
        mov          ah, [edi].cd_draw_sprite_col2.s3clr_clr8
        outb
        mov          al, cur_fg_stack
        mov          ah, [edi].cd_draw_sprite_col2.s3clr_clr16
        outb

        check_vert_retrace
        mov          al, cur_mode
        mov          ah, sprite_16_hwvis
        outb
        jmp          set_end
@@:
        mov          al, cur_bg_stack
        mov          ah, [edi].cd_draw_sprite_col1.s3clr_clr8
        outb
        mov          al, cur_bg_stack
        mov          ah, [edi].cd_draw_sprite_col1.s3clr_clr16
        outb
        mov          al, cur_bg_stack
        mov          ah, [edi].cd_draw_sprite_col1.s3clr_clr24
        outb

        mov          al, cur_fg_stack
        mov          ah, [edi].cd_draw_sprite_col2.s3clr_clr8
        outb
        mov          al, cur_fg_stack
        mov          ah, [edi].cd_draw_sprite_col2.s3clr_clr16
        outb
        mov          al, cur_fg_stack
        mov          ah, [edi].cd_draw_sprite_col2.s3clr_clr24
        outb

        check_vert_retrace
        mov          al, cur_mode
        mov          ah, sprite_24_hwvis
        outb

set_end:

cEnd

        align   4
cProc   SetBTColor,<PUBLIC>

; Set the Color of the cursor

cBegin

        ; select cursor color write
        mov     dx, 3d4h
        mov     al, 55h
        out     dx, al
        inc     dx
        in      al, dx
        and     al, 0FCh
        or      al, 1
        out     dx, al

        ; start from addr 0
        mov     dx, 3c8h
        mov     al, 0
        out     dx, al

        ; set the color
        mov     al, 0
        mov     dx, 3c9h
        out     dx, al
        out     dx, al
        out     dx, al

        ; set background
        mov     al, 0
        out     dx, al
        out     dx, al
        out     dx, al

        ; set foreground
        mov     al, 0ffh
        out     dx, al
        out     dx, al
        out     dx, al

cEnd

        align   4
cProc   BTOutputSprite,<PUBLIC>

; Draws the current sprite at the hot spot specified in the draw_sprite data

cBegin

; Subtract the hot spot offset from the hot spot (x coordinate only).
; edb fix for 73808 - hardware cursor pattern offset should be in pels
; and don't check hw_format 4 times
        mov     ebx, [edi].cd_draw_sprite_x
        mov     eax, [edi].cd_draw_sprite_hot_x
        mov     edx, 0
        add     ebx, 64
        sub     ebx, eax
        jns     @F
        neg     ebx                     ; if off screen then use origin 0
        mov     edx, ebx                ; and nonzero cursor offset
        mov     ebx, 0
        jmp     short bt_x_pos
@@:
        cmp     [edi].cd_screen_hw_format, SIXTEEN_BITS_PER_PEL
        jnz     @F
        ; @DMS dont need to do this for brooktree !!!
        ; probably not for 24bpp either - But we shall see when the time comes.
        ;shl     ebx, 1
@@:
        cmp     [edi].cd_screen_hw_format, TWENTYFOUR_BITS_PER_PEL
        jnz     short bt_x_pos
        mov     eax, ebx
        shl     ebx, 1
        add     ebx, eax

; Write to hardware sprite x
; edx == cursor offset
; ebx == cursor origin
bt_x_pos:
        mov     dx, 3d4h
        mov     al, 55h
        out     dx, al
        inc     dx
        in      al, dx
        and     al, 0FCh
        or      al, 3
        out     dx, al

        mov     dx, 3c8h
        mov     al, bl
        out     dx, al
        mov     dx, 3c9h
        mov     al, bh
        out     dx, al

; Subtract the hot spot offset from the hot spot (y coordinate only).
        mov     ebx, [edi].cd_draw_sprite_y
        add     ebx, 64
        sub     ebx, [edi].cd_draw_sprite_hot_y

; If the result is negative (off top of screen) then negate it
        jns          short bt_y_pos
        neg          ebx

bt_y_pos:
; Write to hardware sprite y
        mov     dx, 3c6h
        mov     al, bl
        out     dx, al
        mov     dx ,3c7h
        mov     al, bh
        out     dx, al

bt_sprite_on:

        ;turn the cursor on
        mov     dx, 3d4h
        mov     al, 55h
        out     dx, al
        inc     dx
        in      al, dx
        and     al, 0FCh
        or      al, 2
        out     dx, al

        mov     dx, 3c9h
        in      al, dx
        and     al, 0fch
        or      al, 2
        out     dx, al

;Reset DAC Select
        mov     dx,3D4h                 ; The default state of the lower
        mov     al,55h                  ; two bits of 3D4/Index 55 is
        out     dx,al                   ; zero.
        inc     dx
        in      al,dx
        and     al,0FCh
        out     dx,al

cEnd

;**********************************************************************/
; DrawSprite
;
;  Inputs:  edi - points to cursor data
;           cursor_data.draw_sprite.x / y
;
;  Destroys: eax, ebx
;
; saves the common palette registers,
; draws the current sprite at the hot spot specified in the draw_sprite data
; and restores the common palette registers.
;
;**********************************************************************/

        align   4
cProc   DrawSprite,<PUBLIC>,<esi,edx>

cBegin
        mov     dx,3D4h         ; We need to save and restore 3D4 here
        in      al,dx           ;  because when doing fills in seamless
        push    ax              ;  Paintbrush, 3D4, index 55 get trashed. @EKF

        ; Make Sure Extended Registers are unlocked
        mov     al, 39h
        mov     ah, 0A0h
        outb

        cmp     [edi].cd_adapter_id, S3_WITH_BROOK_DAC
        jz      draw_bt_sprite

; Now actually draw the sprite
        call    OutputSprite
        jmp     draw_exit
draw_bt_sprite:

        call    BTOutputSprite

draw_exit:
        pop     ax
        mov     dx,3D4h
        out     dx,al

cEnd


;/**********************************************************************
;/* Function:  SetSpriteShape
;/* Purpose:   Passes the mouse pointer definition to the S3 sprite
;/*
;/**********************************************************************

        align   4
cProc   SetSpriteShape,<PUBLIC,NODATA>,<ecx,edi,esi,ebx>

cBegin

; set up the data segment because we currently have 0's in the selectors
        mov     ax, SEG FLAT:_cursor_data
        mov     ds, eax
        mov     es, eax

; ensure we can access the cursor data
        lea     edi, _start_of_cursor_data

        cmp     [edi].cd_adapter_id, S3_WITH_BROOK_DAC
        jz      set_bt_sprite

        ; Make Sure Extended Registers are unlocked
        mov     al, 39h
        mov     ah, 0A0h
        outb

; the easy path is when we simply turn the sprite off
        cmp     [edi].cd_draw_sprite_flags, SPRITE_VISIBLE
        je      short sprite_is_visible

; Set hardware sprite control to sprite_hwinvis to remove the sprite
        check_vert_retrace
        mov     al, cur_mode
        mov     ah, sprite_hwinvis
        outb
        jmp     short sss_exit

sprite_is_visible:
        cld

        mov          bx, [edi].cd_cursor_hwscan
        mov          al, cur_storage_start_lo
        mov          ah, bl
        outb
        mov          al, cur_storage_start_hi
        mov          ah, bh
        outb

; Call OutputSprite (draws current sprite at hot pos)
        call    OutputSprite
        call    SetSprite
        jmp     short sss_exit

set_bt_sprite:

; the easy path is when we simply turn the sprite off
        cmp     [edi].cd_draw_sprite_flags, SPRITE_VISIBLE
        je      short bt_sprite_is_visible

; Set hardware sprite control to sprite_hwinvis to remove the sprite
        mov     dx, 3d4h
        mov     al, 55h
        out     dx, al
        inc     dx
        in      al, dx
        and     al, 0FCh
        or      al, 2
        out     dx, al

        mov     dx, 3c9h
        in      al, dx
        and     al, 0fch
        out     dx, al

;Reset DAC Select
        mov     dx,3D4h                 ; The default state of the lower
        mov     al,55h                  ; two bits of 3D4/Index 55 is
        out     dx,al                   ; zero.
        inc     dx
        in      al,dx
        and     al,0FCh
        out     dx,al


        jmp     short sss_exit

bt_sprite_is_visible:

; Call OutputSprite (draws current sprite at hot pos)
        call    SetBTColor
        call    BTOutputSprite

sss_exit:

cEnd
endif  ; S3

public  _end_of_cursor_code
_end_of_cursor_code:

_CURSOR         ends

END
