;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE     55,132
        TITLE    Pointer Drawing Functions
        SUBTITLE Header
;/*****************************************************************************
;*
;* SOURCE FILE NAME = POINTER.ASM
;*
;* DESCRIPTIVE NAME = Pointer shape routines
;*
;*
;* VERSION      V2.0
;*
;* DATE
;*
;* DESCRIPTION
;*
;*  This file contains the pointer shape routines required to draw the
;*  pointer shape on the VGA.
;*
;*  All display drivers must support a "pointer" for the pointing device.
;*  The pointer is a small graphics image which is allowed to move around
;*  the screen independantly of all other operations to the screen, and is
;*  normally bound to the location of the pointing device.  The pointer is
;*  non-destructive in nature, i.e.; the bits underneath the pointer image
;*  are not destroyed by the presence of the pointer image.
;*
;*  A pointer consists of an AND mask and an XOR mask, which give
;*  combinations of 0's, 1's, display, or inverse display.
;*
;*                   AND XOR | DISPLAY
;*                   ----------------------
;*                    0   0  |    0
;*                    0   1  |    1
;*                    1   0  |   Display
;*                    1   1  | Not Display
;*
;*  The pointer also has a "hot spot", which is the pixel of the pointer
;*  image which is to be aligned with the actual pointing device location.
;*
;*
;*                 |            For a pointer like this, the hot spot
;*                 |            would normally be the *, which would
;*              ---*---         be aligned with the pointing device
;*                 |            position
;*                 |
;*
;*  The pointer may be moved to any location on the screen, be restricted to
;*  only a section of the screen, or made invisible.  Part of the pointer
;*  may actually be off the edge of the screen, and in such a case only the
;*  visible portion of the pointer image is displayed.
;*
;*  Logically, the pointer image isn't part of the physical display surface.
;*  When a drawing operation coincides with the pointer image, the result is
;*  the same as if the pointer image wasn't there.  In reality, if the
;*  pointer image is part of the display surface it must be removed from
;*  memory before the drawing operation may occur, and redrawn at a later
;*  time.
;*
;*  This exclusion of the pointer image is the responsibility of the display
;*  driver.  If the pointer image is part of physical display memory, then
;*  all output operations must perform a hit test to determine if the
;*  pointer must be removed from display memory, and set a protection
;*  rectangle wherein the pointer must not be displayed.  The actual pointer
;*  image drawing routine must honor this protection rectangle by never
;*  drawing the pointer image within its boundary.
;*
;*  This code doesn't distinguish between pointers and icons, they both are
;*  the same size, 32 x 32, which comes out square.
;*
;* Restrictions:
;*
;*  All routines herein assume protection either via CLI/STI or a semaphore
;*  at higher level code.
;*
;*
;* FUNCTIONS    pointer_off
;*              draw_pointer
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   08/28/92              25329  Trap 14 when pointer moved to right
;*                                edge of screen
;*   11/28/92              55718  PM Excel: Cursor is corrupted
;*   01/15/93              59385  Add Cirrus Logic support.
;*
;*****************************************************************************/

        .386P
        .MODEL FLAT,SYSCALL

;/*
;** Include files
;*/

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

        INCLUDE EGAFAM.INC
        INCLUDE POINTER.INC
        INCLUDE ASSERT.MAC
        INCLUDE CURSTRC.INC

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


FLAG_RING0      equ     1
FLAG_RING3      equ     4

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

;/*
;** Data
;*/

_PtrData32 SEGMENT DWORD PUBLIC FLAT 'DATA'

EXTERNDEF CurData:CURSORSTRUCT


save_hw_state_rtns      dd      0
                        dd      offset _PtrCode32:video7_save_hw_state_rtn
                        dd      offset _PtrCode32:trident_save_hw_state_rtn
                        dd      offset _PtrCode32:tseng_save_hw_state_rtn
                        dd      offset _PtrCode32:wd_save_hw_state_rtn
                        dd      offset _PtrCode32:ati_save_hw_state_rtn
                        dd      offset _PtrCode32:spdway_save_hw_state_rtn
                        dd      offset _PtrCode32:cirrus_save_hw_state_rtn ;          

restore_hw_state_rtns   dd      0
                        dd      offset _PtrCode32:video7_restore_hw_state_rtn
                        dd      offset _PtrCode32:trident_restore_hw_state_rtn
                        dd      offset _PtrCode32:tseng_restore_hw_state_rtn
                        dd      offset _PtrCode32:wd_restore_hw_state_rtn
                        dd      offset _PtrCode32:ati_restore_hw_state_rtn
                        dd      offset _PtrCode32:spdway_restore_hw_state_rtn
                        dd      offset _PtrCode32:cirrus_restore_hw_state_rtn ;          

cursor_bank_switch_rtns dd      0
                        dd      offset _PtrCode32:video7_curs_bank_switch_rtn
                        dd      offset _PtrCode32:trident_curs_bank_switch_rtn
                        dd      offset _PtrCode32:tseng_curs_bank_switch_rtn
                        dd      offset _PtrCode32:wd_curs_bank_switch_rtn
                        dd      offset _PtrCode32:ati_curs_bank_switch_rtn
                        dd      offset _PtrCode32:spdway_curs_bank_switch_rtn
                        dd      offset _PtrCode32:cirrus_curs_bank_switch_rtn

_PtrData32 ENDS


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


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

        OPTION  EPILOGUE:NONE
        OPTION  PROLOGUE:NONE

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

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

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

pointer_off ENDP

        OPTION  EPILOGUE:EPILOGUEDEF
        OPTION  PROLOGUE:PROLOGUEDEF

        SUBTITLE draw_pointer
        PAGE +

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

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


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


        mov     ecx,edi
ASSUME  ECX:PCURSORSTRUCT
ASSUME  EBX:PCURSORSTRUCT

        PUSH    EBP                     ; DO NOT AUTOSAVE THIS!
        CLD

;/*
;** Set up the pointer to VRAM  and implement
;** kludge to make multi-chip possible. Since this module is called from both
;** ring 0 and ring 3 all "calls" in it should be relative. However in
;** order to implement multi-chip we needed to initialize a variable to the
;** offset to the correct bank switching and other routines for the particular
;** chip currently in the system. Regardless of which page or page table this
;** code is executing from the 3 least significant nibbles are always the same,
;** so the following code checks to see if the upper nibbles in the already
;** initialized variables are correct, and if not it sets them to the same
;** as the current eip.
;*/

        mov     edi,[ecx].pVRAMRing0
        MOV     AX,CS
        AND     AX,011b
        JNE     init_ring3
        cmp     [ecx].ring_flag,FLAG_RING0
        JE      ptr_vars_right
        jmp     short change_subr_addrs
init_ring3:
        mov     edi,pVRAMInstance       ;invalid at ring0
                                        ;required at ring3
        cmp     [ecx].ring_flag,FLAG_RING3
        je      ptr_vars_right

change_subr_addrs:

        call    @f
@@:
        pop     eax                     ; get our current eip
        and     eax,0fffff000h          ; an isolate page info.
        and     [ecx].curs_save_hw_state_rtn,0fffh  ; set the save state code address
        or      [ecx].curs_save_hw_state_rtn,eax
        and     [ecx].curs_bank_switch_rtn,0fffh      ; set the bank switch code addr
        or      [ecx].curs_bank_switch_rtn,eax
        and     [ecx].curs_restore_hw_state_rtn,0fffh
        or      [ecx].curs_restore_hw_state_rtn,eax

ptr_vars_right:

        mov     [ecx].selScreenPtr,edi

;*************************************************************************

        call    [ecx].curs_save_hw_state_rtn  ; get the initial bank
        LEA     EDI,[ecx].pdPtr0              ; EDI --> new pointer's data
        MOV     ESI,EDI
        ADD     EDI,[ecx].ppdNew
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data

        mov     eax,[ecx].cxPointer
        mov     [edi].RECT_DATA.sizs.sizs_cx,ax ;initialize size of cursor
        mov     eax,[ecx].cyPointer
        mov     [edi].RECT_DATA.sizs.sizs_cy,ax ;assume no clipping
        xor     ax,ax
        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_x,ax
        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_y,ax

        MOV     EAX,ptlX                ; Find index into mask pointer table
        mov     [edi].RECT_DATA.ptlScreen.ptl_x,eax
mov     [edi].POINTER_DATA.rd.ptlScreen.ptl_x,eax
        mov     ebx,PD_VALID            ;Assume visible
        or      eax,eax

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

        js      @f                      ;If X is negative, lhs clipping needed
        jmp     done_x_clipping
@@:
        neg     eax                     ;Want |ax|
        cmp     eax,[ecx].cxPointer
        jl      @f
        jmp     not_visible
@@:
        call    adj_masks_horz

done_x_clipping::
        add     eax,[ecx].cxPointer
        sub     eax,SCREEN_CX
        jle     @f
        sub     [edi].RECT_DATA.sizs.sizs_cx,ax
        cmp     eax,[ecx].cxPointer
        jge     not_visible
        mov     ax,[edi].RECT_DATA.sizs.sizs_cx
        mov     [edi].POINTER_DATA.x_clip,al
        or      ebx, PD_CLIP_RIGHT      ; flag that it needs rhs clipping
@@:

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

        mov     eax,ptlY
        mov     [edi].RECT_DATA.ptlScreen.ptl_y,eax
        or      eax,eax
        js      @f
        jmp     check_bot_clip          ;If Y is negative, top clipping needed
@@:
        neg     eax
        cmp     eax,[ecx].cyPointer
        jl      @f
        jmp     not_visible
@@:
        call    adj_masks_vert_top
        jmp     short done_y_clipping

check_bot_clip::
        add     eax,[ecx].cyPointer
        sub     eax,SCREEN_CY
        jle     done_y_clipping
        sub     [edi].RECT_DATA.sizs.sizs_cy,ax
        cmp     eax,[ecx].cyPointer
        jge     not_visible
        jmp     short done_y_clipping

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

not_visible::
        xor     eax,eax
        mov     [edi].POINTER_DATA.fb,al
        test    [esi].POINTER_DATA.fb,PD_VALID
        jnz     short rectangles_been_computed
        jmp     draw_pointer_exit       ;No new, no old

done_y_clipping:
        mov     [edi].POINTER_DATA.fb,bl                ;Set clipping flags and show valid

rectangles_been_computed:

;/*
;** Calculate any overlap between the new cursor position
;** and the old cursor position.
;*/

        call    calc_rects

        LEA     ESI,[ecx].pdPtr0              ; EDI --> new pointer's data
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data
        mov     dl,[esi].POINTER_DATA.fb
        test    dl,PD_VALID
        jz      done_restoring

;/*
;** restore old saved image here
;*/


        call    restore_image

;/*
;** shift the data in the save buffer either right or left.
;*/


        cmp     byte ptr [ecx].fbFlush, TRUE
        jz      done_restoring

        cmp     byte ptr [ecx].SaveBufferShift, LEFT
        jne     @f
        call    left_just_save
        jmp     short done_restoring
@@:
        call    right_just_save

done_restoring::
        LEA     ESI,[ecx].pdPtr0              ; EDI --> new pointer's data
        ADD     ESI,[ecx].ppdNew              ; ESI --> old pointer's data

        mov     dl,[esi].POINTER_DATA.fb
        test    dl,PD_VALID
        jnz     @F
        jmp     done_showing
@@:

;/*
;** Copy screen image to save buffer
;*/

        call    save_image

;/*
;** Make a copy of the PtrSaveArea,
;** this will be the PtrWorkArea
;*/

        cld
        lea     esi, [ecx].PtrSaveArea
        lea     edi, [ecx].PtrWorkArea

        mov     ah, byte ptr [ecx].PtrSizeBytes.sizs_cy   ; bh = line counter
        mov     dx, [ecx].PtrSizeBytes.sizs_cx    ; dx = line count reset
        movzx   edx,dx
        mov     ebx, PTR_WIDTH * 8
        sub     ebx, edx                            ; bx = offset to next line
        push    ecx
@@:
        mov     ecx, edx                            ; reset line count
        rep     movsb                       ; move a line
        add     esi, ebx                            ; si = next source line
        add     edi, ebx                            ; di = next dest line
        dec     ah                          ; bump the line count
        jnz     @b
        pop     ecx

;/*
;** point to the propper mask set
;*/

        lea     eax, [ecx].BaseAndMasks
        mov     [ecx].and_mask, eax
        lea     eax, [ecx].BaseXorMasks
        mov     [ecx].xor_mask, eax
        lea     eax, [ecx].BaseColorMasks
        mov     [ecx].color_mask, eax

        lea     esi,[ecx].pdPtr0              ; EDI --> new pointer's data
        add     esi,[ecx].ppdNew              ; ESI --> old pointer's data
        test    [esi].POINTER_DATA.fb, PD_CLIP_LEFT or PD_CLIP_TOP
        jz      @f

        lea     eax, [ecx].clipped_and_mask
        mov     [ecx].and_mask, eax
        lea     eax, [ecx].clipped_xor_mask
        mov     [ecx].xor_mask, eax
        lea     eax, [ecx].clipped_color_mask
        mov     [ecx].color_mask, eax
@@:

;/*
;** do the AND mask
;*/

        push    ebp                     ; bp will be used for dest next line offset

        mov     esi, [ecx].and_mask             ; si = pointer mask bit AND data
        lea     edi, [ecx].PtrWorkArea  ; di = image from screen
        mov     ah, byte ptr [ecx].PtrAndMask.sizs_cy   ; ah = pointer height counter
        mov     bx, PTR_WIDTH
        sub     bx, [ecx].PtrAndMask.sizs_cx    ; bx = next source line offset
        movzx   ebx,bx
        mov     bp, PTR_WIDTH * 8
        sub     bp, [ecx].PtrSizeBytes.sizs_cx ; bp = next dest line offset
        movzx   ebp,bp

and_ptr_line_loop::
        mov     dh, byte ptr [ecx].PtrAndMask.sizs_cx   ; ch = width of line in bytes
and_ptr_byte_loop:
        mov     dl, 8                   ; cl = bits in a byte
        lodsb                           ; al = source and byte
and_ptr_bit_loop:
        rcl     al, 1                   ; shift a bit into carry
        jc      and_ptr_ff              ; is the bit set?
        and     byte ptr [edi], 0       ; nope, AND in a 0
        jmp     short @f
and_ptr_ff:
        and     byte ptr [edi], 0ffh            ; yep, AND in ones
@@:
        inc     edi                     ; di = next dest

        dec     dl                      ; bump bit count
        jnz     and_ptr_bit_loop
        dec     dh                      ; bump byte count
        jnz     and_ptr_byte_loop
        add     esi, ebx                        ; si = next line of dest
        add     edi, ebp                        ; di = next line of dest
        dec     ah                      ; bump line count
        jnz     and_ptr_line_loop

        ; do the XOR mask

        mov     esi, [ecx].xor_mask             ; si = pointer mask bit XOR data
        lea     edi, [ecx].PtrWorkArea  ; di = image from screen
        mov     ah, byte ptr [ecx].PtrXorMask.sizs_cy   ; ah = pointer height counter
        mov     bx, PTR_WIDTH
        sub     bx, [ecx].PtrXorMask.sizs_cx    ; bx = offset to next line
        movzx   ebx,bx
        mov     bp, PTR_WIDTH * 8
        sub     bp, [ecx].PtrSizeBytes.sizs_cx ; bp = next dest line offset
        movzx   ebp,bp

xor_ptr_line_loop::
        mov     dh, byte ptr [ecx].PtrXorMask.sizs_cx   ; ch = width of line in bytes
xor_ptr_byte_loop:
        mov     dl, 8                   ; cl = bits in a byte
        lodsb                           ; al = source and byte
xor_ptr_bit_loop:
        rcl     al, 1                   ; shift a bit into carry
        jc      xor_ptr_ff              ; is the bit set?
        xor     byte ptr [edi], 0               ; nope, AND in a 0
        jmp     short @f
xor_ptr_ff:
        xor     byte ptr [edi], 0ffh            ; yep, AND in ones
@@:
        inc     edi                     ; di = next dest

        dec     dl                      ; bump bit count
        jnz     xor_ptr_bit_loop
        dec     dh                      ; bump byte count
        jnz     xor_ptr_byte_loop
        add     esi, ebx                        ; si = next line of source
        add     edi, ebp                        ; di = next line of dest
        dec     ah                      ; bump line count
        jnz     xor_ptr_line_loop

        pop     ebp                     ; restore the frame

        ; If there is a color mask then OR the colors in.

        cmp     byte ptr [ecx].fbPointer, FBPTR_COLOR
        jnz     dpc_done

        movzx   eax, [ecx].PtrColorMask.sizs_cy ;           
        mov     dl,al                           ;            no. lines to OR
        movzx   eax, [ecx].PtrColorMask.sizs_cx
        mov     dh,al                           ;            bytes/line
        mov     ebx,PTR_WIDTH * 8               ;           
        sub     ebx,eax                         ;            bytes to skip

        lea     edi, [ecx].PtrWorkArea
        mov     esi, [ecx].color_mask

        push    ecx
        xor     ecx,ecx                         ;           
        mov     cl,dh                           ;            save sizs_cx
dpc_loop:
        lodsb                                   ; al = color value
        or      byte ptr [edi], al              ; OR color value into work area
        inc     edi                             ; advance to next byte in work area
        dec     dh                              ;           
        jnz     dpc_loop                        ;           
        add     edi,ebx                         ;            skip unused bytes
        add     esi,ebx                         ;            skip unused bytes
        mov     dh,cl                           ;            reset counter
        dec     dl                              ;            next line
        jnz     dpc_loop                        ;           
        pop     ecx
dpc_done::

;/*
;** now move the image back to the screen
;**
;** set the initial bank and the initial line with in the bank
;** for the restore destination.
;*/

        lea     esi,[ecx].pdPtr0              ; EDI --> new pointer's data
        add     esi,[ecx].ppdNew              ; ESI --> old pointer's data

        push ebp                        ; bp used to hold current bank

        xor     edx,edx
        mov     eax,10000h
        mov     ebx,SCREEN_CBSCAN
        div     ebx
        mov     [ecx].scan_per_bank,ax
        mov     [ecx].dst_scan_line_left,ax

        xor     edx,edx
        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_y
        div     word ptr [ecx].scan_per_bank                            ; ax = bank, dx = line in bank
        sub     [ecx].dst_scan_line_left,dx
        mov     [ecx].curbank,ax
        xchg    ax, dx
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        mov     bp,SCREEN_CBSCAN
        mul     bp                              ; ax = y addr
        movzx   eax,ax
        add     eax, [esi].RECT_DATA.ptlScreen.ptl_x
        add     eax,[ecx].selScreenPtr
        mov     edi,eax

        lea     esi,[ecx].pdPtr0              ; EDI --> new pointer's data
        add     esi,[ecx].ppdNew              ; ESI --> old pointer's data
        mov     bp, [ecx].PtrSizeBytes.sizs_cx ; cx = restore width in bytes
        test    byte ptr [esi].POINTER_DATA.fb, PD_CLIP_LEFT
        jz      @f
        mov     bp, [esi].RECT_DATA.sizs.sizs_cx
@@:
        test    byte ptr [esi].POINTER_DATA.fb, PD_CLIP_RIGHT
        jz      @f
        xor     ah,ah
        mov     al,[esi].POINTER_DATA.x_clip
        cmp     ax,bp           ;Is cursor fix in right size ?
        jge     @f              ;Yes.
        mov     bp, ax          ; 
@@:
        movzx   ebp,bp
        mov     ebx, PTR_WIDTH * 8
        sub     ebx, ebp                        ; bx = next source line offset
        mov     edx, SCREEN_CBSCAN      ; dx = scan line width
        sub     edx, ebp                        ; dx = next dest line offset
        mov     ax, bp                  ; al = line reset value
        mov     ah, byte ptr [ecx].PtrSizeBytes.sizs_cy ; bx = restore height

;/*
;** set the source addr
;*/

        lea     esi, [ecx].PtrWorkArea  ; ds:si = New source addr

;/*
;** do the new pointer
;*/

new_loop:
        push    ecx
        mov     ecx,ebp
        and     cx,0ffh
        rep     movsb                   ; restore a line
        pop     ecx
        dec     ah                              ; bump the Y count
        jz      new_ptr_exit            ; exit at end of Y

        mov     bp, ax                  ; reset width count
        add     esi, ebx                        ; si = next source line
        add     edi, edx                        ; di = next scan line addr
        dec     [ecx].dst_scan_line_left
        jnz     bank_ok2
        push    [ecx].scan_per_bank
        pop     [ecx].dst_scan_line_left
        ; Switch banking detected in destination
        push    edx
        inc     [ecx].curbank
        mov     dx,[ecx].curbank
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        sub     edi,10000h
        pop     edx
bank_ok2:
        jmp short new_loop

new_ptr_exit:
        pop ebp

done_showing::

;/*
;** swap the old and new pointer rectangle stuctures
;*/

        mov     eax,[ecx].ppdNew
        xchg    eax,[ecx].ppdOld
        mov     [ecx].ppdNew,eax

        call    [ecx].curs_restore_hw_state_rtn      ; set the bank back to original


draw_pointer_exit:
        pop     ebp                     ;DO NOT AUTOSAVE THIS

        ret
draw_pointer ENDP



adj_masks_horz::
        or      ebx, PD_CLIP_LEFT       ; flag that it needs lhs clipping

;/*
;** set the clip rectangle
;*/

        mov     dword ptr [edi].RECT_DATA.ptlScreen.ptl_x,0
        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_x,ax
        sub     word ptr [edi].RECT_DATA.sizs.sizs_cx,ax

        push    ebx                     ; save flags

;/*
;** build a set of clip masks
;** copy the AND, XOR, and COLOR masks to their own areas
;*/

        mov     ebx,ecx
ASSUME  EBX:PCURSORSTRUCT
        mov     ecx, (PTR_HEIGHT * PTR_WIDTH) / 4
        lea     esi, [ebx].BaseAndMasks ; set the offsets
        lea     edi, [ebx].clipped_and_mask
        rep     movsd                   ; do the move

        mov     ecx, (PTR_HEIGHT * PTR_WIDTH) / 4
        lea     esi, [ebx].BaseXorMasks ; set the offsets
        lea     edi, [ebx].clipped_xor_mask
        rep     movsd                   ; do the move

        mov     ecx, (PTR_HEIGHT * PTR_WIDTH * 8) / 4
        lea     esi, [ebx].BaseColorMasks ; set the offsets
        lea     edi, [ebx].clipped_color_mask
        rep     movsd                   ; do the move

        mov     ecx,ebx
        mov     bh,al                   ; bh = shift left count

;/*
;** shift the AND mask
;*/


        mov     bl, PTR_HEIGHT          ; bl = AND mask line counter

        lea     esi, [ecx].clipped_and_mask ; si = offset of left hand side mask
        mov     edi, esi                        ; dest = source

lhs_and_mask_loop:
        lodsw                           ; ax = left hand word of mask
        xchg    ah, al
        mov     dx, ax                  ; dx = left hand word of mask
        lodsw                           ; ax = right hand word of mask
        xchg    ah, al

        push    bx
@@:
        stc                             ; set the carry flag
        rcl     ax, 1                   ; rotate the lower 17 bits
        rcl     dx, 1                   ; rotate the upper 17 bits
        dec     bh                      ; bump the rotate count
        jnz     @b
        pop     bx

        xchg    ax, dx                  ; ax = shifted left hand bits
        xchg    ah, al
        stosw                           ; put them back
        xchg    ax, dx                  ; ax = shifted right hand bits
        xchg    ah, al
        stosw                           ; put them back
        dec     bl                      ; bump the mask line count
        jnz     lhs_and_mask_loop       ; do the entier mask

;/*
;** now do the XOR mask
;*/

        lea     esi, [ecx].clipped_xor_mask ; si = offset of left hand side mask
        mov     edi, esi                        ; dest = source

        mov     bl, PTR_HEIGHT          ; bl = XOR mask line counter
lhs_xor_mask_loop:
        lodsw                           ; ax = left hand word of mask
        xchg    ah, al
        mov     dx, ax                  ; dx = left hand word of mask
        lodsw                           ; ax = right hand word of mask
        xchg    ah, al

        push    bx
@@:
        clc                             ; clear the carry flag
        rcl     ax, 1                   ; rotate the lower 17 bits
        rcl     dx, 1                   ; rotate the upper 17 bits
        dec     bh                      ; bump the rotate count
        jnz     @b
        pop     bx

        xchg    ax, dx                  ; ax = shifted left hand bits
        xchg    ah, al
        stosw                           ; put them back
        xchg    ax, dx                  ; ax = shifted right hand bits
        xchg    ah, al
        stosw                           ; put them back
        dec     bl                      ; bump the mask line count
        jnz     lhs_xor_mask_loop       ; do the entier mask

;/*
;** Now do the Color Mask
;**   di = left edge of cursor
;**   si = di + shift left count
;**   cx = cursor width - shift left count for move
;**   cx = shift left count for fill
;**   dx = width of cursor in bytes
;**   bx = shift left count
;**   ah = cursor height counter
;**   al = fill value
;*/

        mov     ah, PTR_HEIGHT          ; ah = cursor height counter
        mov     al, 0                   ; al = fill value for right side
        mov     bl, bh                  ; bl = shift left count
        mov     bh, 0                   ; bh = 0 extension for bl
        movzx   ebx,bx
        mov     edx, PTR_WIDTH * 8      ; dx = width of cursor in bytes
        lea     edi, [ecx].clipped_color_mask   ; di = left edge of cursor
        mov     esi, edi
        add     esi, ebx                        ; si = new left edge, after shift

        push    ecx
lhs_color_mask_loop:
        mov     ecx, edx                        ; cx = cursor width
        sub     ecx, ebx                        ; cx = bytes to shift left
        rep     movsb                   ; shift the mask left
        mov     ecx, ebx                        ; cx = bytes to fill
        rep     stosb                   ; fill the right side of the mask
        add     esi, ebx                        ; si = next line of mask
        dec     ah                      ; dec the line count
        jnz     lhs_color_mask_loop     ; shift the entire cursor
        pop     ecx

        LEA     EDI,[ecx].pdPtr0              ; EDI --> new pointer's data
        MOV     ESI,EDI
        ADD     EDI,[ecx].ppdNew
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data

        pop     ebx                     ; restore flags
        mov     ax, [edi].RECT_DATA.sizs.sizs_cx ; restore |X|
        ret


adj_masks_vert_top::
        mov     dword ptr [edi].RECT_DATA.ptlScreen.ptl_y,0
        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_y,ax
        sub     word ptr [edi].RECT_DATA.sizs.sizs_cy,ax

;/*
;** The Top needs to be clipped.
;** Only if there is no left hand side clipping should copy
;** the ptr masks into the clip masks.
;*/


        or      ebx, PD_CLIP_TOP        ; Set the Top Clip Flag
        push    ebx                     ; Save the flags

        test    ebx, PD_CLIP_LEFT       ; Are the Clip mask set for lhs
        mov     ebx,ecx
        jnz     @f

;/*
;** build a set of clip masks
;** copy the AND, XOR, and COLOR masks to their own area
;*/

        mov     ecx, (PTR_HEIGHT * PTR_WIDTH) / 4
        lea     esi, [ebx].BaseAndMasks ; set the offsets
        lea     edi, [ebx].clipped_and_mask
        rep     movsd                   ; do the move

        mov     ecx, (PTR_HEIGHT * PTR_WIDTH) / 4
        lea     esi, [ebx].BaseXorMasks ; set the offsets
        lea     edi, [ebx].clipped_xor_mask
        rep     movsd                   ; do the move

        mov     ecx, (PTR_HEIGHT * PTR_WIDTH * 8) / 4
        lea     esi, [ebx].BaseColorMasks ; set the offsets
        lea     edi, [ebx].clipped_color_mask
        rep     movsd                   ; do the move
@@:

;/*
;** Shift and Fill the AND mask
;*/

        mov     edx, eax                        ; dx = lines to skip, temp variable
        mov     ecx, eax
        shl     ecx, 2                  ; cx = number of bytes to skip
        mov     eax, (PTR_HEIGHT * PTR_WIDTH) ; ax = bytes in mask
        sub     eax, ecx                        ; ax = number of bytes to shift
        push    eax
        xchg    eax, ecx                        ; cx = shift count, ax = skip count
        lea     edi, [ebx].clipped_and_mask
        mov     esi, edi
        add     esi, eax                        ; si = offset of new first line
        rep     movsb                   ; shift the mask

        pop     esi
        lea     edi, [ebx].clipped_and_mask
        add     edi, esi                        ; di = offset of mask fill
        mov     ecx, eax                        ; cx = byte fill count
        shr     ecx, 2                  ; cx = word fill count
        mov     eax, -1                 ; mask fill value
        rep     stosd                   ; fill the mask

        ; Shift and Fill the XOR mask

        mov     ecx, edx                        ; cx = lines to skip
        shl     ecx, 2                  ; cx = byte skip count
        mov     eax, (PTR_HEIGHT * PTR_WIDTH) ; ax = bytes in mask
        sub     eax, ecx                        ; ax = bytes to shift
        push    eax
        xchg    eax, ecx                        ; cx = shift count, ax = skip count
        lea     edi, [ebx].clipped_xor_mask
        mov     esi, edi
        add     esi, eax                        ; si = offset of new first line
        rep     movsb                   ; shift the mask

        pop     esi
        lea     edi, [ebx].clipped_xor_mask
        add     edi, esi                ; di = offset of mask fill
        mov     ecx, eax                        ; words to fill
        shr     ecx, 2                  ; bytes to fill
        mov     eax, 0h                 ; mask fill value
        rep     stosd                   ; fill the mask

;/*
;** Now do the Color Mask, register usage
;**   On entry dx = lines to skip
;**   bx = lines to skip * cursor width in bytes
;**   di = top edge of cursor
;**   si = di + bx
;**   cx = cursor size in bytes - bx, for move
;**   cx = bx, for fill
;**   al = 0, fill value
;*/

        mov     eax, PTR_WIDTH * 8      ; ax = cursor width in bytes
        mul     edx                     ; ax = bytes to skip

        lea     edi, [ebx].clipped_color_mask ; di = top edge of mask
        mov     esi, edi
        add     esi, eax                        ; si = start of new top

        mov     ecx, PTR_WIDTH * 8 * PTR_HEIGHT
        sub     ecx, eax                        ; bytes to move up

        rep     movsb                   ; do the move

        mov     ecx, eax                        ; cx = bytes to fill
        xor     al,al
        rep     stosb                   ; do the fill

        mov     ecx,ebx
        LEA     EDI,[ecx].pdPtr0              ; EDI --> new pointer's data
        MOV     ESI,EDI
        ADD     EDI,[ecx].ppdNew
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data

        pop     ebx                     ; restore flags
        mov     ax, [edi].RECT_DATA.sizs.sizs_cy ; restore |Y|
        ret


calc_rects::

;/*
;** set up the pointers to the pointer stuctures
;*/

        LEA     EDI,[ecx].pdPtr0              ; EDI --> new pointer's data
        MOV     ESI,EDI
        ADD     EDI,[ecx].ppdNew
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data

;/*
;** If there is left clipping in effect then leave the rectangles
;** alone.  We will want to use the last Old dimensions.
;*/


        test    [edi].POINTER_DATA.fb, PD_CLIP_LEFT
        jz      @f
        jmp     cr_disjoint
@@:

;/*
;** If the OLD cursor is now invalid then treat as if disjoint
;*/


        test    [esi].POINTER_DATA.fb, PD_VALID
        jnz     @f
        jmp     cr_disjoint
@@:

;/*
;** If there has been any Y movement then treat as disjoint.
;*/

        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_y    ; ax = old screen X
        mov     edx, [edi].RECT_DATA.ptlScreen.ptl_y    ; dx = new screen X
        cmp     eax, edx
        jne     cr_disjoint

;/*
;** Handle any X overlap first.
;*/

        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_x    ; ax = old screen X
        mov     edx, [edi].RECT_DATA.ptlScreen.ptl_x    ; dx = new screen X

;/*
;** Is the new X coordinate to the left of the old X coordinate
;*/

        cmp     edx, eax
        jg      cr_new_x_greater

;/*
;** The new X is to the left of the old X
;**
;** Moving the cursor to the left will expose the old image on
;** the right side of where the old cursor was.
;** The source for the image data comes from the right side
;** of the save buffer.
;**
;** We must also get new data for the save buffer from the left
;** side of the old cursor.  This will be the area the new cursor
;** will obscure.
;**
;** First test if there is any overlap of the old and new cursor
;** positions.  If there is no overlap then the cursors are considered
;** disjoint.
;*/

        sub     eax, edx                            ; ax = exposed image width
        mov     bx, [ecx].PtrSizeBytes.sizs_cx
        movzx   ebx,bx
        dec     ebx
        cmp     eax, ebx
        jge     cr_disjoint

;/*
;** There was an overlap.
;** ax = the amount of the overlap
;*/

        mov     [esi].RECT_DATA.sizs.sizs_cx, ax    ; cxSave from save to screen
        mov     [edi].RECT_DATA.sizs.sizs_cx, ax    ; cxSave from screen to save

;/*
;** Determine the delta X from the new pointers position on the screen
;** where we will begin copying new data into the save buffer from.
;** Since we're moving left the delta X position is 0.
;*/

        xor     ax,ax
        mov     word ptr [edi].RECT_DATA.ptsDelta.ptw_x, ax

;/*
;** Determine the X position in the save buffer that the new image data
;** will be copied into.  Since we're moving left this will be 0.
;*/


        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_x, ax

;/*
;** Determine the X position in the save buffer that the old image data
;** will be copied from, to restore what was exposed by moving the
;** cursor.  This is pointer width - x distance moved.
;*/

        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        sub     ax, [esi].RECT_DATA.sizs.sizs_cx
        mov     word ptr [esi].RECT_DATA.ptwSave.ptw_x, ax

;/*
;** We will need to shift the data in the save buffer to the right
;** in order to make room for the new image data from the display
;** on the left side of the save buffer.
;*/

        mov     byte ptr [ecx].SaveBufferShift, RIGHT   ; set the save buffer shift direction
        mov     byte ptr [ecx].fbFlush, FALSE
        jmp     short x_done
cr_new_x_greater:

;/*
;** The new x is to the right of the old x
;**
;** Moving the cursor to the right will expose the old image on
;** the left side of where the old cursor was.
;** The source for the image data comes from the left side
;** of the save buffer.
;**
;** We must also get new data for the save buffer from the right
;** side of the old cursor.  This will be the area the new cursor
;** will obscure.
;**
;** First test if there is any overlap of the old and new cursor
;** positions.  If there is no overlap then the cursors are considered
;** disjoint.
;*/


        sub     edx, eax
        mov     bx, [ecx].PtrSizeBytes.sizs_cx
        dec     bx
        movzx   ebx,bx
        cmp     edx, ebx
        jge     cr_disjoint

;/*
;** dx = the amount of overlap.  This is also the area exposed
;** when the old cursor is moved and the area obscured by the new
;** cursor.
;*/

        mov     [esi].RECT_DATA.sizs.sizs_cx, dx        ; cxSave from save to screen
        mov     [edi].RECT_DATA.sizs.sizs_cx, dx        ; cxSave from screen to save

;/*
;** Determine the delta X from the new pointers position on the screen
;** where we will begin copying new data into the save buffer from.
;** Since we're moving righ the delta X position is:
;**   the width of the cursor - x distance moved, 0 relative
;*/

        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        movzx   eax,ax
        sub     eax, edx
        mov     word ptr [edi].RECT_DATA.ptsDelta.ptw_x, ax

;/*
;** Determine the X position in the save buffer that the new image data
;** will be copied into.  Since the new image data will be
;** to the right of the old image it will always be copied to the
;** right side of the save buffer.
;** This is: width of save buffer - x distance moved
;*/

        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        sub     ax, [edi].RECT_DATA.sizs.sizs_cx
        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_x, ax

;/*
;** Determine the X position in the save buffer that the old image
;** will be restored from. This will always be the left edge, 0.
;*/

        mov     word ptr [esi].RECT_DATA.ptwSave.ptw_x, 0

;/*
;** Set the direction the save buffer data must be shifted
;*/

        mov     byte ptr [ecx].SaveBufferShift, LEFT    ; set the save buffer shift direction
        mov     byte ptr [ecx].fbFlush, FALSE

        jmp     short x_done

cr_disjoint:

;/*
;** The Old and New Cursors do not overlap
;*/

        mov     word ptr [esi].RECT_DATA.ptwSave.ptw_x, 0               ; New x = left edge of save
        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        mov     [esi].RECT_DATA.sizs.sizs_cx, ax ; New cx

        mov     word ptr [edi].RECT_DATA.ptwSave.ptw_x, 0               ; New x = left edge of save
        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        mov     [edi].RECT_DATA.sizs.sizs_cx, ax                ; New cx

        mov     word ptr [edi].RECT_DATA.ptsDelta.ptw_x, 0

        mov     byte ptr [ecx].fbFlush, TRUE

x_done:
        ret


restore_image::

;/*
;** set the initial bank and the initial line with in the bank
;** for the restore destination.
;*/

        push ebp                        ; bp used to hold current bank

        xor     edx,edx
        mov     eax,10000h
        mov     ebx,SCREEN_CBSCAN
        div     ebx
        mov     [ecx].scan_per_bank,ax
        mov     [ecx].dst_scan_line_left,ax

        xor     edx,edx
        mov     eax, [esi].RECT_DATA.ptlScreen.ptl_y
        div     word ptr [ecx].scan_per_bank                            ; ax = bank, dx = line in bank
        sub     [ecx].dst_scan_line_left,dx
        mov     [ecx].curbank,ax
        xchg    ax, dx
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        mov     bp,SCREEN_CBSCAN
        mul     bp                              ; ax = y addr
        movzx   eax,ax
        add     eax, [esi].RECT_DATA.ptlScreen.ptl_x
        mov     dx, word ptr [esi].RECT_DATA.ptwSave.ptw_x      ; add in the save offset
        movzx   edx,dx
        mov     ebx,[esi].RECT_DATA.ptlScreen.ptl_x ;CMVC_25329
        add     ebx,edx                 ;CMVC_25329
        add     eax,edx
        mov     edi,[ecx].selScreenPtr
        add     edi,eax
        mov     dx, word ptr [esi].RECT_DATA.ptwSave.ptw_x      ; the save offset
        movzx   edx,dx
        mov     bp, [esi].RECT_DATA.sizs.sizs_cx        ; cx = restore width in bytes
        test    [esi].POINTER_DATA.fb,PD_CLIP_RIGHT
        jz      @f
        xor     ah,ah
        mov     al,[esi].POINTER_DATA.x_clip
        cmp     ax,bp           ;Is cursor fix in right size ?
        jge     @f              ;Yes.
        mov     bp, ax          ; 
@@:
        movzx   ebp,bp
        add     ebx,ebp                 ;CMVC_25329
        sub     ebx,SCREEN_CBSCAN       ;CMVC_25329
        jbe     @f                      ;CMVC_25329
        sub     ebp,ebx                 ;CMVC_25329
        ja      @f                      ;CMVC_25329
        jmp     ptr_541                 ;CMVC_25329
@@:                                     ;CMVC_25329

;/*
;** set the source addr
;*/


        lea     esi, [ecx].PtrSaveArea  ; ds:si = restore source addr
        add     esi, edx                        ; add in the x offset

;/*
;** set up the loop varaiables
;*/

        mov     eax, PTR_WIDTH * 8
        sub     eax, ebp                        ; ax = offset to next line in save buff
        mov     bx, bp                  ; bl = scan line reset count
        mov     bh, byte ptr [ecx].PtrSizeBytes.sizs_cy ; bh = resotre height
        mov     edx,SCREEN_CBSCAN
        sub     edx, ebp                        ; dx = screen scan line offset

;/*
;** do the restore
;*/


restore_loop:
        push    ecx
        mov     ecx,ebp
        and     cx,0ffh
        rep     movsb                   ; restore a line
        pop     ecx
        dec     bh                              ; bump the Y count
        jz      ptr_541                 ; exit at end of Y

        mov     bp, bx                  ; reset width count
        add     esi, eax                        ; si = next save scan line addr
        add     edi, edx                        ; di = next screen scan line addr
        dec     [ecx].dst_scan_line_left
        jnz     bank_ok
        push    [ecx].scan_per_bank
        pop     [ecx].dst_scan_line_left

;/*
;** Switch banking detected in destination
;*/

        push    edx
        inc     [ecx].curbank
        mov     dx,[ecx].curbank
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        sub     edi,10000h
        pop     edx
bank_ok:
        jmp short restore_loop

ptr_541:
        pop ebp
        ret


left_just_save:

;/*
;** Set up the loop variables
;*/

        LEA     ESI,[ecx].pdPtr0              ; EDI --> new pointer's data
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data
        mov     dx, [esi].RECT_DATA.sizs.sizs_cx ; dx = left shift factor
        movzx   edx,dx

        lea     edi, [ecx].PtrSaveArea  ; di = base of PtrSaveArea
        mov     esi, edi
        add     esi, edx                        ; si = ptr to new right data

        mov     ax, [ecx].PtrSizeBytes.sizs_cx ; ax = cursor width in bytes
        movzx   eax,ax
        sub     eax, edx                        ; ax = bytes to move per line
        mov     edx, PTR_WIDTH * 8      ; dx = bitmap pitch
        sub     edx, eax                        ; dx = delta to begin addr for next line
        mov     bh, byte ptr [ecx].PtrSizeBytes.sizs_cy ; bh = height of cursor
        push    ecx
        mov     ecx, eax                        ; cx = line count
ljs_loop:
        rep     movsb                   ; shift the line
        add     edi, edx                        ; di = next line of dest
        add     esi, edx                        ; si = next line of source
        mov     ecx, eax                        ; reload counter
        dec     bh                      ; increment line count
        jnz     ljs_loop                ; do the entire cursor
        pop     ecx

        ret


right_just_save:
        std                             ; counting down here

;/*
;** Set up the loop variables
;*/

        LEA     ESI,[ecx].pdPtr0              ; EDI --> new pointer's data
        ADD     ESI,[ecx].ppdOld              ; ESI --> old pointer's data
        mov     dx, [esi].RECT_DATA.sizs.sizs_cx ; dx = right shift factor
        movzx   edx,dx
        lea     edi, [ecx].PtrSaveArea  ; di = base of PtrSaveArea
        mov     ax, [ecx].PtrSizeBytes.sizs_cx
        movzx   eax,ax
        dec     eax
        add     edi, eax
        mov     esi, edi
        sub     esi, edx                        ; si = ptr to new right data

        mov     ax, [ecx].PtrSizeBytes.sizs_cx ; ax = cursor width in bytes
        movzx   eax,ax
        sub     eax, edx                        ; ax = bytes to move per line
        mov     edx, PTR_WIDTH * 8      ; dx = bitmap pitch
        add     edx, eax                        ; dx = delta to begin addr for next line
        mov     bh, byte ptr [ecx].PtrSizeBytes.sizs_cy
        push    ecx
        mov     ecx, eax                        ; cx = line count

rjs_loop:
        rep     movsb                   ; shift the line
        add     edi, edx                        ; di = next line of dest
        add     esi, edx                        ; si = next line of source
        mov     ecx, eax                        ; reload counter
        dec     bh                      ; increment line count
        jnz     rjs_loop                ; do the entire cursor
        pop     ecx

        cld
        ret



save_image:

;/*
;** set the initial bank and the initial line with in the bank
;** for the save area source
;*/

        push ebp                        ; bp used to hold current bank

        xor     edx,edx
        mov     eax,10000h
        mov     ebx,SCREEN_CBSCAN
        div     ebx
        mov     [ecx].scan_per_bank,ax
        mov     [ecx].dst_scan_line_left,ax

        xor     edx,edx
        mov     ax, word ptr [esi].RECT_DATA.ptlScreen.ptl_y
        div     word ptr [ecx].scan_per_bank                            ; ax = bank, dx = line in bank
        sub     [ecx].dst_scan_line_left,dx
        mov     [ecx].curbank,ax
        xchg    ax, dx
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        mov     bp,SCREEN_CBSCAN
        mul     bp                              ; ax = y addr
        movzx   eax,ax
        add     eax, [esi].RECT_DATA.ptlScreen.ptl_x
        mov     dx, word ptr [esi].RECT_DATA.ptsDelta.ptw_x
        movzx   edx,dx
        mov     ebx,[esi].RECT_DATA.ptlScreen.ptl_x ;CMVC_25329
        add     ebx,edx                 ;CMVC_25329
        add     eax,edx
        add     eax,[ecx].selScreenPtr
        mov     dx, word ptr [esi].RECT_DATA.ptwSave.ptw_x
        movzx   edx,dx
        mov     bp, [esi].RECT_DATA.sizs.sizs_cx        ; cx = restore width in bytes
        movzx   ebp,bp
        mov     esi, eax                        ; es:si = save source addr
        add     ebx,ebp                 ;CMVC_25329
        sub     ebx,SCREEN_CBSCAN       ;CMVC_25329
        jbe     @f                      ;CMVC_25329
        sub     ebp,ebx                 ;CMVC_25329
        ja      @f                      ;CMVC_25329
        jmp     ptr_606                 ;CMVC_25329
@@:                                     ;CMVC_25329

;/*
;** set the dest addr
;*/

        lea     edi, [ecx].PtrSaveArea  ; ds:si = restore source addr
        add     edi, edx

;/*
;** set up the loop varaiables
;*/


        mov     eax, PTR_WIDTH * 8
        sub     eax, ebp                        ; ax = offset to next line in save buff
        mov     bx, bp                  ; bl = scan count reset value
        mov     bh, byte ptr [ecx].PtrSizeBytes.sizs_cy ; bx = resotre height
        mov     edx,SCREEN_CBSCAN
        sub     edx, ebp                        ; dx = next screen scan line offset

;/*
;** do the save
;*/

save_loop:
        push    ecx
        mov     ecx,ebp
        and     cx,0ffh

;/*
;**       This is to fix a     in the i386 chip!
;**       rep     movsb                   ; save a line
;*/

        jecxz   skip_loop
@@:
        movsb                   ; save a line
        loop    @b
skip_loop:
        pop     ecx
        dec     bh                              ; bump the Y count
        jz      ptr_606                         ; exit at end of Y

        mov     bp, bx                          ; reset the width
        add     edi, eax                        ; di = next save scan line addr
        add     esi, edx                        ; si = next screen scan line addr
        dec     [ecx].dst_scan_line_left
        jnz     bank_ok1
        push    [ecx].scan_per_bank
        pop     [ecx].dst_scan_line_left

;/*
;** Switch banking detected in destination
;*/

        push    edx
        inc     [ecx].curbank
        mov     dx,[ecx].curbank
        call    [ecx].curs_bank_switch_rtn      ; set the bank
        sub     esi,10000h
        pop     edx
bank_ok1:
        jmp short save_loop

ptr_606:

        pop ebp
        ret



video7_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,3c4h
        mov     al,0e8h
        out     dx,al
        inc     dx
        in      al,dx
        mov      [ecx].hw_state[0] ,al

        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


wd_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,03ceh
        mov     al,9
        out     dx,al
        inc     dx
        in      al,dx
        mov     [ecx].hw_state[0],al    ;save PR0A
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


ati_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,01ceh
        mov     al,0b2h
        out     dx,al
        inc     dx
        in      al,dx
        mov     [ecx].hw_state[0],al
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 

public trident_save_hw_state_rtn
trident_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,03C4H
        mov     al,0eh
        out     dx,al
        inc     dx
        in      al,dx
        xor     al,2
        mov     [ecx].hw_state[0],al
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


spdway_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,[ecx].bank_reg
        in      al,dx
        mov     [ecx].hw_state[0],al
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


tseng_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,03CDh        ; 
        in      al,dx           ;Read in segment read/write register.
        mov     [ecx].hw_state[0],al    ;Save save segment register.
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 

; start           
cirrus_save_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,03ceh
        mov     al,9
        out     dx,al
        inc     dx
        in      al,dx
        mov     [ecx].hw_state[0],al    ;save PR0A
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 
; end           


video7_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,3c4h
        mov     al,0e8h
        mov     ah,[ecx].hw_state[0]
        out     dx,ax
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


wd_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,3ceh
        mov     ah,[ecx].hw_state[0]    ;restore PR0A
        mov     al,9
        out     dx,ax
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


ati_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,01ceh
        mov     al,0b2h
        mov     ah,[ecx].hw_state[0]
        out     dx,ax
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 

public trident_restore_hw_state_rtn
trident_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,03C4H
        mov     al,0eh
        mov     ah,[ecx].hw_state[0]
        out     dx,ax
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 

spdway_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     al,[ecx].hw_state[0]
        mov     dx,[ecx].bank_reg
        out     dx,al
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 


tseng_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     al,[ecx].hw_state[0]    ;Get read/write segment.
        mov     dx,03CDh        ; 
        out     dx,al           ;Restore read/write segment.
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 

; start           
cirrus_restore_hw_state_rtn:

        push    ax              ;Save registers used in this routine.
        push    dx              ; 
        mov     dx,3ceh
        mov     ah,[ecx].hw_state[0]    ;restore PR0A
        mov     al,9
        out     dx,ax
        pop     dx              ;Recover registers.
        pop     ax              ; 
        ret                     ; 
; end           

;/*
;**       Set A000:0000 to point to a specific bank of 64K
;**
;**       Entry:  DL = bank select
;**       Exit:   none
;*/


;/*
;**
;**       Assume: VRAM VGA in extended 256 color mode
;**               Extensions are enabled
;**
;*/

video7_curs_bank_switch_rtn:

        push    ax
        push    dx
        shl     dl,4
        mov     ah,dl
        mov     dx,03c4h
        mov     al,0e8h
        out     dx,ax
        pop     dx
        pop     ax
        ret


wd_curs_bank_switch_rtn:
        push    ax              ; 
        push    dx              ; 

        mov     ah,dl           ; 
        shl     ah,4            ;Move to upper nibble for 64k chunk.
        mov     dx,03Ceh        ; 
        mov     al,9
        out     dx,ax

        pop     dx              ; 
        pop     ax              ; 
        ret


ati_curs_bank_switch_rtn:
        push    ax
        push    dx
        mov     dh,dl
        shl     dh,4
        rol     dh,1
        shl     dl,1
        or      dh,dl
        mov     ah,dh
        mov     dx,01ceh
        mov     al,0b2h
        out     dx,ax
        pop     dx              ; 
        pop     ax              ; 
        ret                     ; 

public trident_curs_bank_switch_rtn
trident_curs_bank_switch_rtn:

        push    ax
        push    dx
        Mov       ah, dl             ; 
        Mov       DX, 03C4H          ; 
        Mov       AL, 0EH            ; 
        Out       DX, AL
        Inc       DX
        In        AL, DX
        And       AL, 0F0H           ; Mask out bank bits
        Or        AL, ah             ; Or in new bank bits
        Xor       AL, 2
        Out       DX, AL
        pop     dx
        pop     ax
        ret


spdway_curs_bank_switch_rtn:

        push    ax
        push    dx
        mov     al,dl
        mov     dx,[ecx].bank_reg
        out     dx,al
        pop     dx
        pop     ax
        ret


tseng_curs_bank_switch_rtn:
        push    ax
        push    dx
        mov     al,dl           ; 
        shl     al,4            ;Move to upper nibble for read segment.
        or      al,dl           ;Or in lower nibble for write segment.
        mov     dx,03CDh        ; 
        out     dx,al           ; 
        pop     dx              ; 
        pop     ax              ; 
        ret                     ; 

; start           
cirrus_curs_bank_switch_rtn:

        push    ax              ; 
        push    dx              ; 

        mov     ah,dl           ; 
        shl     ah,4            ;Move to upper nibble for 64k chunk.
        mov     dx,03Ceh        ; 
        mov     al,9
        out     dx,ax

        pop     dx              ; 
        pop     ax              ; 
        ret                     ; 
; end           

_PtrCode32 ENDS

public  done_x_clipping
public  check_bot_clip
public  not_visible
public  done_restoring
public  and_ptr_line_loop
public  xor_ptr_line_loop
public  dpc_done
public  done_showing
public  adj_masks_horz
public  adj_masks_vert_top
public  calc_rects
public  restore_image
public  left_just_save
public  save_image

        END
