;*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.;
;*****************************************************************************/
ifdef DCAF
        PAGE    ,132
        TITLE   SCBOUNDS.ASM
;/*****************************************************************************
;*
;* SOURCE FILE NAME = SCBOUNDS.ASM
;*
;* DESCRIPTIVE NAME = DCAF screen bounds accumulation functions
;*
;*
;* VERSION      V2.0
;*
;* DATE         04/28/92
;*
;* DESCRIPTION  Contains functions to update/manipulate Screen Change Areas.
;*
;* FUNCTIONS
;*              AccumulateScreenBounds
;*              SeamlessExcludeCursor
;*              ResetScreenBounds
;*              SetFullScreenBounds
;*              MergeSeamlessSCAWithGlobalSCAs
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/28/92                     Created for DCAF 1.3
;*   07/01/92                     Ported to 32-bit for DCAF 2.0
;*
;*****************************************************************************/

        .xlist
        .386p
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1
        include pmgre.inc
        include driver.inc
        include protos.inc
        include extern.inc
        include assert.mac

        include dcafmac.inc
        .list

        .MODEL FLAT
        ASSUME  CS:FLAT, SS:FLAT, DS:FLAT, ES:FLAT

        .DATA

;/*
;** Define the SCA to be used by Seamless Windows.
;** We give Seamless addressability to this structure at
;** initialisation time. All Seamless bounds are accumulated
;** into this structure. On every GetScreenChangeArea call,
;** this structure is checked, and if it has been updated by
;** Seamless then the rectangles are added into all of the
;** "user" ("application") SCAs, and the Seamless structure is
;** reset.
;*/

;/*
;** An alternative approach is to give Seamless addressability to
;** the whole of the Driver's heap (where the user SCAs are
;** allocated) - but this would leave us vunerable to attack
;** from rogue apps !!
;*/

;/*
;** Initialise the pNext and cRects fields to zero.
;*/
SeamlessSCA  SCA < 0, 0 >
        .ERRNZ  SCA.sca_next
        .ERRNZ  SCA.sca_cRects-4


        .CODE
ALIGN 4

;/*
;** --------------------Internal Routine----------------------------------
;*************************************************************************
;** SeamlessExcludeCursor is called (via a thunk) by Seamless Windows,
;** notifying us that drawing is about to take place in the specified
;** rectangle.
;**
;** We accumulate this rectangle into the Seamless SCA (see comment above).
;**
;** The call is then passed on to the exclude cursor routine (far_exclude).
;*************************************************************************
;*/
SeamlessExcludeCursor PROC SYSCALL

LOCAL   rclExclusionRect :RECTL,
        pSavedSCA        :DWORD

;/*
;** Check if DCAF Active  MRC PERF 01/10/94
;*/

        cmp     fDCAFEnabled,TRUE
        jne     sec_no_dcaf

;/*
;** Save parameters
;*/
        push    ecx
        push    edx
        push    esi
        push    edi

;/*
;** Store clipped xLeft
;*/
        xor     eax,eax         ; A zero will be useful!
        cmp     ecx,eax         ; xLeft >= 0 ?
        jge     @F
        xor     ecx,ecx
@@:
        mov     rclExclusionRect.rcl_xLeft, ecx

;/*
;** Store clipped xRight
;*/
        mov     eax,SCREEN_CX
        dec     eax
        cmp     esi,eax         ; xRight <= SCREEN_CX-1 ?
        jle     @F
        mov     esi,eax
@@:
        mov     rclExclusionRect.rcl_xRight, esi

;/*
;** Clip, flip and store yTop
;*/
        mov     eax,SCREEN_CY
        dec     eax
        cmp     edx,0           ; yTop >= 0 ?
        jge     @F
        xor     edx,edx
@@:
        sub     eax,edx         ; Flip yTop
        mov     rclExclusionRect.rcl_yTop, eax

;/*
;** Clip, flip and store yBottom
;*/
        mov     eax,SCREEN_CY
        dec     eax
        cmp     edi,eax         ; yBottom <= SCREEN_CY-1 ?
        jle     @F
        mov     edi,eax
@@:
        sub     eax,edi         ; Flip yBottom
        mov     rclExclusionRect.rcl_yBottom, eax

;/*
;** We now have the Exclusion rectangle set up.
;** We alter the pStartSCA pointer to point at the static
;** Seamless SCA (which was granted Seamless addressability
;** at initialisation time).
;*/
        mov     eax, pStartSCA
        mov     pSavedSCA, eax
        lea     eax, offset FLAT:SeamlessSCA
        mov     pStartSCA, eax

        INVOKE  AccumulateScreenBound, ADDR rclExclusionRect

;/*
;** Restore the original (true) pStartSCA.
;*/
        mov     eax, pSavedSCA
        mov     pStartSCA, eax

;/*
;** Restore parameters
;*/
        pop     edi
        pop     esi
        pop     edx
        pop     ecx

;/*
;** Call on to the exclude cursor routine
;*/

sec_no_dcaf:

        INVOKE  far_exclude

        ret
SeamlessExcludeCursor ENDP



;--------------------------Internal-Routine-----------------------------;
;/*********************************************************************
;* AccumulateScreenBound                                              *
;*                                                                    *
;* This routine takes the passed rectangle, and accumulates it into   *
;* all the current ScreenChangeAreas.                                 *
;*                                                                    *
;* When possible the passed rectangle has been pre-clipped, to avoid  *
;* unnecessary areas being included in the bounds.                    *
;*                                                                    *
;* The passed rectangle is in inclusive SCREEN coords.                *
;*********************************************************************/
;*
;* Entry:
;*       None
;* Returns:
;*       None
;* Error Returns:
;*       None
;* Registers Preserved:
;*       16 bit  SI,DI,BP,DS,ES
;*       32 bit  ESI,EDI,EBX,EBP,DS,ES,FS,GS
;* Registers Destroyed:
;*       16 bit  AX,BX,CX,DX,FLAGS
;*       32 bit  EAX,ECX,EDX,FLAGS
;* History:
;*
;* Wed 13-May-1992       -by-    Mark Berry
;*     Converted it from scbounds.c
;*********************************************************************


;/*
;** CalculateSize does the maths to calculate the size of a rectangle.
;** The rectptr argument is a (near) pointer to the rectangle.
;** The result is returned in eax.
;*/
CalculateSize   macro   rectptr
        mov     eax,[rectptr].rcl_yTop
        sub     eax,[rectptr].rcl_yBottom
        mov     edx,[rectptr].rcl_xRight
        sub     edx,[rectptr].rcl_xLeft
        mul     edx
endm

;/*
;** MINIMUM returns in eax the mimimum of its 2 arguments.
;** MAXIMUM returns in eax the maximum of its 2 arguments.
;** edx will be corrupted.
;** All numbers are positive.
;*/
MINIMUM macro   parm1, parm2
        mov     eax, parm2
        sub     eax, parm1
        sbb     edx, edx
        and     eax, edx
        add     eax, parm1
endm

MAXIMUM macro   parm1, parm2
        mov     eax, parm2
        sub     eax, parm1
        cmc
        sbb     edx, edx
        and     eax, edx
        add     eax, parm1
endm




ALIGN 4

AccumulateScreenBound PROC SYSCALL USES edi esi ebx,
        prclArgBound :DWORD

LOCAL   MergedRect :RECTL,
        IntersectedRect :RECTL,
        BestMergedRect :RECTL,
        ulPair1 :DWORD,
        ulPair2 :DWORD,
        pBestPair1 :DWORD,
        pBestPair2 :DWORD,
        ulBestPair1 :DWORD,
        ulBestPair2 :DWORD,
        ulBestSize :DWORD,
        ulBestIncrease :DWORD,
        ulMergedSize :DWORD,
        ulIntersectedSize :DWORD,
        rclArgBound :RECTL

;/*
;** Pick up the start of the linked list of SCAs
;*/
        mov     ebx,pStartSCA
        ASSUME  EBX:PTR SCA
        or      ebx,ebx
        jz      reached_terminator

;/*
;** Take a local copy of the passed bound, so that we can change it.
;** We change it to make it exclusive.
;*/
        mov     esi,prclArgBound
        ASSUME  ESI:PTR RECTL
        mov     eax,[esi].rcl_xLeft
        mov     rclArgBound.rcl_xLeft,eax
        mov     eax,[esi].rcl_yBottom
        mov     rclArgBound.rcl_yBottom,eax
        mov     eax,[esi].rcl_xRight
        inc     eax             ; change from inclusive to exclusive
        mov     rclArgBound.rcl_xRight,eax
        mov     eax,[esi].rcl_yTop
        inc     eax             ; change from inclusive to exclusive
        mov     rclArgBound.rcl_yTop,eax

ifdef FIREWALLS
;/*
;** Check that the rectangle fits on the screen.
;*/
        cmp     rclArgBound.rcl_xLeft,0
        jl      oh_dear
        cmp     rclArgBound.rcl_yBottom,0
        jl      oh_dear
        cmp     rclArgBound.rcl_xRight,0
        jl      oh_dear
        cmp     rclArgBound.rcl_yTop,0
        jl      oh_dear
        cmp     rclArgBound.rcl_xLeft,SCREEN_CX
        jg      oh_dear
        cmp     rclArgBound.rcl_yBottom,SCREEN_CY
        jg      oh_dear
        cmp     rclArgBound.rcl_xRight,SCREEN_CX
        jg      oh_dear
        cmp     rclArgBound.rcl_yTop,SCREEN_CY
        jg      oh_dear
        jmp     tests_ok

oh_dear:
        int     3

tests_ok:
endif ; FIREWALLS


for_each_SCA:
;/*
;** Currently:
;** ebx  -> the current SCA in the linked list
;*/

;/*
;** Pick up the rectangle count
;*/
        mov     ecx,[ebx].sca_cRects
        jecxz   not_contained

;/*
;** See if the passed rectangle is contained by one of the
;** existing rectangles.
;*/
        lea     edi,[ebx].sca_rect-size RECTL ; point at rect[-1]! in the SCA
        ASSUME  EDI:PTR RECTL

each_rect:
        add     edi,size RECTL  ; move to the next rectangle

        mov     eax,rclArgBound.rcl_yTop
        cmp     eax,[edi].rcl_yTop
        jg      next_rect

        mov     eax,rclArgBound.rcl_xRight
        cmp     eax,[edi].rcl_xRight
        jg      next_rect

        mov     eax,rclArgBound.rcl_yBottom
        cmp     eax,[edi].rcl_yBottom
        jl      next_rect

        mov     eax,rclArgBound.rcl_xLeft
        cmp     eax,[edi].rcl_xLeft
        jl      next_rect

;/*
;** To have got here the rectangle was fully contained in the
;** one in the SCA.  Thus we move to the next SCA.
;*/
        jmp     next_SCA

next_rect:
        loop    each_rect

not_contained:
;/*
;** To have fallen through to here, we now know that the passed
;** rectangle is not full contained in any of the existing ones.
;*/

;/*
;** We store the new rectangle at the end of the existing ones.
;**
;** This may only be temporary if we are already at our maximum
;** number of stored rectangles.
;**
;** We know the rectangle we are copying holds USHORT values in
;** LONG locations, so we can simply ignore the high words.
;*/
        mov     ecx,[ebx].sca_cRects
        lea     edi,[ebx].sca_rect ; point at the rects in the SCA structure
        ASSUME  EDI:PTR RECTL
        mov     eax,ecx
        shl     eax,4           ; mult by size RECTL
        .erre   size RECTL EQ 16
        add     edi,eax         ; move to after the last existing rectangle

        mov     eax,rclArgBound.rcl_xLeft
        mov     [edi].rcl_xLeft,eax
        mov     eax,rclArgBound.rcl_yBottom
        mov     [edi].rcl_yBottom,eax
        mov     eax,rclArgBound.rcl_xRight
        mov     [edi].rcl_xRight,eax
        mov     eax,rclArgBound.rcl_yTop
        mov     [edi].rcl_yTop,eax

;/*
;** Calculate the size of this rectangle, and store it
;*/
        CalculateSize edi

        .erre   type SCA.sca_size EQ 4
        mov     DWORD PTR [ebx].sca_size[ecx*4], eax

;/*
;** If we have less than our full number of rectangles then we
;** can just add the new one.
;*/
        cmp     ecx,NUM_SCA_RECTS
        jge     full_number_held

;/*
;** Its easy to just increment the count here, since we still
;** have room for this rectangle.
;*/
        inc     [ebx].sca_cRects
        jmp     next_SCA

full_number_held:
;/*
;** This is the complex case.
;** We have no room for an extra rectangle, so must combine 2
;** of our existing rectangles.
;**
;** The current register usage is:
;** ebx     -> the current SCA
;**
;** In addition, within the outer_pair and inner_pair loops, the
;** register usage is
;**
;** eax     general working register
;** edx     general working register
;** ebx     -> the current SCA
;** ecx     sca_cRects
;** esi     points at pair1 rectangle
;** edi     points at pair2 rectangle
;** also used as a general index register
;*/
        mov     eax,-1          ; -1 == the largest unsigned number !
        mov     ulBestIncrease,eax

        xor     eax,eax        ; outer loop is zero to cRects - 1
        mov     ulPair1,eax
        lea     esi,[ebx].sca_rect
        ASSUME  ESI:PTR RECTL

each_outer_pair:
        mov     eax,ulPair1
        inc     eax             ; inner loop is ulPair1 + 1 to cRects
        mov     ulPair2,eax
        mov     edi,esi
        add     edi,size RECTL  ; point at rect[ulPair1 + 1]
        ASSUME  EDI:PTR RECTL

each_inner_pair:
;/*
;** Find the merged area and its size.
;*/
        MINIMUM [esi].rcl_xLeft, [edi].rcl_xLeft
        mov     MergedRect.rcl_xLeft,eax
        MAXIMUM [esi].rcl_xRight, [edi].rcl_xRight
        mov     MergedRect.rcl_xRight,eax
        MAXIMUM [esi].rcl_yTop, [edi].rcl_yTop
        mov     MergedRect.rcl_yTop,eax
        MINIMUM [esi].rcl_yBottom, [edi].rcl_yBottom
        mov     MergedRect.rcl_yBottom,eax

        CalculateSize   MergedRect
        mov     ulMergedSize,eax

;/*
;** Find the rectangles' intersection, and its size.
;*/
        MAXIMUM [esi].rcl_xLeft, [edi].rcl_xLeft
        mov     IntersectedRect.rcl_xLeft,eax
        MINIMUM [esi].rcl_xRight, [edi].rcl_xRight
        mov     IntersectedRect.rcl_xRight,eax
        MINIMUM [esi].rcl_yTop, [edi].rcl_yTop
        mov     IntersectedRect.rcl_yTop,eax
        MAXIMUM [esi].rcl_yBottom, [edi].rcl_yBottom
        mov     IntersectedRect.rcl_yBottom,eax

;/*
;** If we have a badly ordered rectangle, then the size is zero.
;*/
        mov     eax,IntersectedRect.rcl_xRight
        cmp     eax,IntersectedRect.rcl_xLeft
        jbe     its_null

        mov     eax,IntersectedRect.rcl_yTop
        cmp     eax,IntersectedRect.rcl_yBottom
        jnbe    must_calculate

its_null:
        xor     eax,eax         ; size is zero
        jmp     got_size

must_calculate:
        CalculateSize   IntersectedRect
got_size:
        mov     ulIntersectedSize,eax

;/*
;** Find the effect of merging these 2 rectangles.
;** change = mergesize + intersectsize - size[pair1] - size[pair2]
;*/
        mov     eax,ulMergedSize
        add     eax,ulIntersectedSize

        push    edi             ; we need a spare index register
        mov     edi,ulPair1
        lea     edi,[ebx].sca_size[edi*4]
        ASSUME  EDI:PTR DWORD
        sub     eax,[edi]
        mov     edi,ulPair2
        lea     edi,[ebx].sca_size[edi*4]
        ASSUME  EDI:PTR DWORD
        sub     eax,[edi]
        pop     edi             ; restore the index register

;/*
;** Is this better (ie a smaller increase) than the best so far ?
;*/
        cmp     eax,ulBestIncrease
        jae     next_inner_pair

its_better:
;/*
;** It's better, so update our best values.
;*/
        mov     ulBestIncrease,eax
        mov     pBestPair1,esi
        mov     pBestPair2,edi
        mov     eax,ulPair1
        mov     ulBestPair1,eax
        mov     eax,ulPair2
        mov     ulBestPair2,eax
        mov     eax,ulMergedSize
        mov     ulBestSize,eax

;/*
;** Update the best rectangle copy.
;*/
        mov     eax,MergedRect.rcl_xLeft
        mov     BestMergedRect.rcl_xLeft,eax
        mov     eax,MergedRect.rcl_yBottom
        mov     BestMergedRect.rcl_yBottom,eax
        mov     eax,MergedRect.rcl_xRight
        mov     BestMergedRect.rcl_xRight,eax
        mov     eax,MergedRect.rcl_yTop
        mov     BestMergedRect.rcl_yTop,eax

next_inner_pair:
        add     edi,size RECTL  ; move to next rectangle
        inc     ulPair2
        cmp     ulPair2,ecx
        jg      next_outer_pair
        jmp     each_inner_pair

next_outer_pair:
        add     esi,size RECTL  ; move to next rectangle
        inc     ulPair1
        cmp     ulPair1,ecx
        jge     use_the_best
        jmp     each_outer_pair

use_the_best:
;/*
;** Copy the best merged rectangle to the ulBestPair1 location.
;*/
        mov     edi,pBestPair1
        ASSUME  EDI:PTR RECTL
        mov     eax,BestMergedRect.rcl_xLeft
        mov     [edi].rcl_xLeft,eax
        mov     eax,BestMergedRect.rcl_yBottom
        mov     [edi].rcl_yBottom,eax
        mov     eax,BestMergedRect.rcl_xRight
        mov     [edi].rcl_xRight,eax
        mov     eax,BestMergedRect.rcl_yTop
        mov     [edi].rcl_yTop,eax

;/*
;** Copy the size of this merged rectangle.
;*/
        mov     eax,ulBestSize
        push    edi             ; we need a spare index register
        mov     edi,ulBestPair1
        lea     edi,[ebx].sca_size[edi*4]
        ASSUME  EDI:PTR DWORD
        mov     [edi],eax
        pop     edi             ; restore the edi pointer

;/*
;** If we were merging 2 existing rectangles together, then
;** move the new rectangle to the space located by the second of
;** the merged rectangles
;*/
        mov     edi,pBestPair2
        ASSUME  EDI:PTR RECTL
        lea     esi,[ebx].sca_rect ; point at the rects in the SCA structure
        mov     eax,ecx
        shl     eax,4           ; mult by size RECTL
        .erre   size RECTL EQ 16
        add     esi,eax         ; move to after the last existing rectangle
        cmp     esi,edi
        je      move_done       ; BestPair2 was the new rectangle, so dont move

;/*
;** Copy the new rectangle to the ulBestPair2 location.
;*/
        mov     eax,[esi].rcl_xLeft
        mov     [edi].rcl_xLeft,eax
        mov     eax,[esi].rcl_yBottom
        mov     [edi].rcl_yBottom,eax
        mov     eax,[esi].rcl_xRight
        mov     [edi].rcl_xRight,eax
        mov     eax,[esi].rcl_yTop
        mov     [edi].rcl_yTop,eax

;/*
;** Copy the size of this new rectangle.
;*/
        push    edi             ; We need a spare index register
        ; Pick up the size
        mov     eax,DWORD PTR [ebx].sca_size[ecx*4]

        ; Now work out where to copy it to
        mov     edi,ulBestPair2
        lea     edi,[ebx].sca_size[edi*4]
        ASSUME  EDI:PTR DWORD
        mov     [edi],eax
        pop     edi             ; Restore the edi pointer

move_done:
ifdef FIREWALLS
;/*
;** Zero the 'spare' rectangle space, which held the new rectangle.
;*/
        xor     eax,eax
        mov     [esi].rcl_xLeft,eax
        mov     [esi].rcl_yBottom,eax
        mov     [esi].rcl_xRight,eax
        mov     [esi].rcl_yTop,eax
endif ; FIREWALLS


next_SCA:
        mov     ebx, [ebx].sca_next
        or      ebx,ebx
        jz      reached_terminator
        jmp     for_each_SCA

reached_terminator:
        ret

AccumulateScreenBound ENDP


;/*********************************************************************
;** The ResetScreenBounds function sets all of the currently active
;** Screen Change Areas to be NULL.
;*********************************************************************/
ALIGN 4
ResetScreenBounds PROC SYSCALL

        mov     ebx,pStartSCA
        or      ebx,ebx
        jz      no_scas

        xor     eax,eax   ; A zero will be useful.
next_sca:
;/*
;** Set the Screen Change Area to contain zero rectangles.
;*/
        mov     [ebx].sca_cRects,eax
        mov     ebx,[ebx].sca_next
        or      ebx,ebx
        jnz     next_sca

no_scas:
        ret

ResetScreenBounds ENDP


;/*********************************************************************
;** The SetFullScreenBounds function sets all of the currently active
;** SCAs to contain a single rectangle that covers the whole screen.
;** This is called by Resurrection, because the whole screen will be
;** repainted, and setting the bounds to cover the whole screen BEFORE
;** redrawing will cause the bounds to be accumulated quicker.
;*********************************************************************/
ALIGN 4
SetFullScreenBounds PROC SYSCALL

LOCAL   rclFullScreen :RECTL

;/*
;** Reset all of the screen bounds
;*/
        INVOKE  ResetScreenBounds

;/*
;** Now add a single rectangle the size of the screen
;*/
        xor     eax,eax          ; A zero will be useful
        mov     rclFullScreen.rcl_xLeft,eax
        mov     rclFullScreen.rcl_yBottom,eax

;       !! I would like to do the following, but it isn't assembled/loaded
;       !! properly (the high word is FFFF), so we have to resort to
;       !! a more long winded method of doing the same thing.
;       mov     rclFullScreen.rcl_xRight,SCREEN_CX-1
;       mov     rclFullScreen.rcl_yTop,SCREEN_CY-1

        mov     eax,SCREEN_CX
        dec     eax
        mov     rclFullScreen.rcl_xRight,eax
        mov     eax,SCREEN_CY
        dec     eax
        mov     rclFullScreen.rcl_yTop,eax

        INVOKE  AccumulateScreenBound, ADDR rclFullScreen

        ret
SetFullScreenBounds ENDP


;/*********************************************************************
;** MergeSeamlessSCAWithGlobalSCAs adds all of the rectangles within
;** the Seamless SCA to the global (application) SCAs.
;*********************************************************************/
ALIGN 4
MergeSeamlessSCAWithGlobalSCAs PROC SYSCALL

add_next_rect:
        lea     ebx,SeamlessSCA
        ASSUME  ebx:PTR SCA
        mov     eax,[ebx].sca_cRects
        or      eax,eax
        jz      no_more_rects

;/*
;** We have at least 1 rectangle to add to the application
;** SCAs, which we do by calling AccumulateScreenBound.
;** We traverse the rectangle list in reverse order.
;*/

;/*
;** First update the number of rectangles and store.
;*/
        dec     eax
        mov     [ebx].sca_cRects,eax

;/*
;** We have the zero-based index of the last rectangle in eax.
;** Convert this into the address of the rectl.
;*/
        shl     eax,4           ; Multiply by size RECTL
        .erre   size RECTL EQ 16
        lea     eax,[ebx].sca_rect[eax]
        ASSUME  eax:PTR RECTL

;/*
;** Convert the rectangle from exclusive to inclusive
;*/
        dec     [eax].rcl_xRight
        dec     [eax].rcl_yTop

        INVOKE  AccumulateScreenBound,eax

;/*
;** Loop back to see if there are more rects.
;*/
        jmp     add_next_rect


no_more_rects:
        ret

MergeSeamlessSCAWithGlobalSCAs ENDP


;/*********************************************************************
;** Check that all of the bounds accumulation code will be within
;** the code that we made addressable to Seamless Windows.
;*********************************************************************/
        .errnz $-SeamlessExcludeCursor GT 4096

SeamlessCodeEnd label DWORD


endif ; DCAF

end
