;*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    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = SSB.ASM 
;*
;* DESCRIPTIVE NAME = Save/Restore screen bits
;*
;*
;* VERSION      V2.0
;*
;* DATE         05/19/87
;*
;* DESCRIPTION  The function SaveScreenBits saves a rectangle of bits                 
;*              from the display to unused display memory, returning a handle         
;*              to the caller.  It returns an error if either the memory is           
;*              nonexistent or is not available for use.                              
;*                                                                                    
;*              The function RestoreScreenBits discards the saved bits and/or         
;*              restores them to the display.  It returns an error if the             
;*              memory is either nonexistent, or there are not any bits saved.        
;*                                                                                    
;*              Restrictions:
;*              
;*              Several rectangles may be saved at any one time dependent on          
;*              the amount of offscreen space we have available.  The hdc             
;*              passed in must be the screen DC.                                      
;*              
;* FUNCTIONS    SaveScreenBits     
;*              RestoreScreenBits  
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/18/88                     Author:  Geoff Shapiro [gssc!geoffs] 
;*****************************************************************************/

        .xlist
        include cmacros.inc
INCL_GRE_BITMAPS        equ     1
INCL_DDICOMFLAGS        equ     1
        include pmgre.inc
DINCL_SAVE_SCREEN_BITS  equ     1
        include driver.inc
        include display.inc
        include 8514.inc
        include 8514mem.inc
        include njmp.mac
        .list


        externFP far_exclude
        externFP far_unexclude


SsbCache        struc
SsbRects        dw      0               ; # of divided rects
SsbPartial      dw      0               ; # of scans in last partial rect
SsbIncr         dw      0               ; width of each subrectangle
SsbHeight       dw      0               ; height of each full subrectangle
SsbClipLeft     dw      0               ; amt input area clipped on left
SsbClipRight    dw      0               ; amt input area clipped on right
SsbClipTop      dw      0               ; amt input area clipped on top
SsbClipBottom   dw      0               ; amt input area clipped on bottom
SsbCache        ends
        .errnz  size SsbCache and 1


sBegin  PtrData
        externB screen_busy             ; cursor hw exclusion semaphore
sEnd    PtrData


sBegin  Data
        externW sdScreen                ; framebuffer surface definition
        externW SsbX                    ; x origin of ssb save area
        externW SsbY                    ; y origin of ssb save area
        externW SsbXExt                 ; width of ssb save area
        externW SsbYExt                 ; height of ssb save area

;/*
;**  Values needed to maintain track of current utilization of ssb buffer:
;*/

SsbUse  label   word
        SsbCache        <0,0,0,0,0,0,0,0>; initially ssb not utilized

sEnd    Data


sBegin  Code
        assumes cs,Code

        externW CodeData
        externW MyPtrCodeData                     ; for access to pointer data segment

        externNP enter_driver
        externNP leave_driver

;/***************************************************************************
;*
;* FUNCTION NAME = SaveScreenBits 
;*
;* DESCRIPTION   = 
;*
;*        Save a rectangle of bits to unused display memory.
;*       
;*        Registers Preserved:                   
;*              SI,DI,DS,BP                      
;*        Registers Destroyed:                   
;*              AX,BX,CX,DX,ES,FLAGS             
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = handle to saved bits   
;* RETURN-ERROR  = AX = 0 if unable to save bits 
;*
;**************************************************************************/

CMD_SSB =       (\
                 CMD_C_CRECT+CMD_FV_FIX+CMD_DX+CMD_DY+CMD_MA_ACCESS+\
                 CMD_PA_FOUR+CMD_RW_W\
                )

        check   SaveScreenBits,<hdc,prcl,hddc,ulFunN>

        assumes ds,nothing
        assumes es,nothing

cProc   SaveScreenBits,<PUBLIC,FAR,NODATA>,<si,di>

        parmD   hdc
        parmD   lpRect                  ; --> rectangle of bits to save
        parmD   hddc
        parmD   FunN

        localW  loc_exclude_x1          ; exclusion rectangle corners
        localW  loc_exclude_y1
        localW  loc_exclude_x2
        localW  loc_exclude_y2

        localW  loc_x_origin            ; DC origin in x
        localW  loc_y_origin            ; DC origin in y

        localW  loc_clip_left           ; amount clipped on left
        localW  loc_clip_right          ; amount clipped on right
        localW  loc_clip_top            ; amount clipped on top
        localW  loc_clip_bottom         ; amount clipped on bottom
        localW  loc_y_extent            ; width of rectangle
        localW  loc_x_extent            ; height of rectangle

cBegin
        fw_zero <ds,es>
        cld
        mov     ds,CodeData
        assumes ds,Data
        mov     dx,hddc.lo
        call    enter_driver
        njc     ssb_exit_no_lock        ; DX:AX = 0 on error


;/*
;**  check for the ssb cache being empty -- if it isn't return an error...
;*/

        mov     cx,SsbUse.SsbIncr       ; if width of subrect <> 0 then utilized
        jcxz    @F                      ; cache not currently utilized...

;/*
;** No empty slots found, ergo no space available to do internal save screen
;** bits. Return an error so that the save screen bits we be done externally.
;*/

ssb_error_exit:
        sub     ax,ax                   ; error return
        jmp     ssb_exit                ; ssb area already occupied...
@@:

;/*
;**  Read the DDC, moving needed values into local variables.
;*/

        mov     si,hddc.lo              ; load DC origin into local frame
        ddc?    si
        mov     di,[si].ddc_npsd
        test    [di].sd_fb,SD_DEVICE
        jz      ssb_error_exit          ; don't let them save from a bitmap!

        les     si,[si].ddc_prddc       ;DS:SI = RDDC
        assumes es,nothing
        rddc?   es,si

        mov     ax,es:[si].rddc_ptsOrg.pts_x
        mov     loc_x_origin,ax
        mov     ax,es:[si].rddc_ptsOrg.pts_y
        mov     loc_y_origin,ax

        mov     si,ds
        mov     es,si
        assumes es,Data
        lds     si,lpRect               ;DS:SI --> rectangle boundaries
        assumes ds,nothing

;/*
;**  Y-coordinate computations:
;** 
;**  Results will be:
;** 
;**        loc_exclude_y1 = upper limit of exclusion rectangle
;**        loc_exclude_y2 = lower limit of exclusion rectangle
;**        loc_y_extent    = clipped number of scan lines to copy
;** 
;**  Clipping to the display borders is convenient because the
;**  coordinates get flipped.  First, check for negative coords,
;**  which after flipping would be beyond the high edge of the screen;
;**  then, after the flip, check for negative coords again.  The
;**  DC origin is added in before any clipping.
;** 
;**  The amount clipped off the left, right, top, and bottom is saved to
;**  check for valid restore calls.
;*/


ifdef FIREWALLS

;/*
;**    Check for a valid rectangle.  In other words, the lower
;**    left corner must really be the first pair of points.
;*/

        mov     ax,[si].wrc_yTop
        sub     ax,[si].wrc_yBottom
        jl      ssb_no_way_jose
        mov     ax,[si].wrc_xRight
        sub     ax,[si].wrc_xLeft
        jge     ssb_get_on_with_it
ssb_no_way_jose:
        rip     text,<Rectangle corners misordered>
ssb_get_on_with_it:
endif

        mov     bx,sdScreen.sd_cy       ; used more than once
        dec     bx                      ; 0-based

        mov     ax,[si].wrc_yTop        ; get y2
        add     ax,loc_y_origin         ; adjust by DC origin
        js      ssb_exit_error_vect     ; invalid rectangle
        neg     ax                      ; flip to screen coords
        add     ax,bx                   ; 
        mov     di,ax                   ; save flipped top y
        cwd                             ; y2 <-- max(y2,0)
        not     dx
        and     ax,dx                   ; AX = max(y2,0)
        neg     di
        add     di,ax                   ; amount clipped off top
        mov     loc_clip_top,di         ; save top clip amount
        inc     ax                      ; make inclusive rectangle
        mov     loc_exclude_y1,ax       ; save for cursor exclusion
        mov     cx,ax                   ; store in CX for extent calculation

        mov     ax,[si].wrc_yBottom     ; get y1
        add     ax,loc_y_origin         ; adjust by DC origin
        mov     di,ax
        cwd                             ; create AND mask based on sign(y1)
        not     dx                      ; do the clipping
        and     ax,dx                   ; AX = max(y1,0)
        sub     di,ax
        mov     loc_clip_bottom,di      ; amount clipped from bottom
        neg     ax                      ; flip to screen coords
        add     ax,bx
        js      ssb_exit_error_vect     ; invalid rectangle
        mov     loc_exclude_y2,ax       ; save for cursor exclusion

        sub     ax,cx                   ; compute y extent
        jnc     ssb_y_extent_ok         ; if infinitely thin, get out


ssb_exit_error_vect:
        sub     ax,ax                   ; return 0 for error
        mov     ds,CodeData             ; restore DS for leave_driver
        assumes ds,Data
        jmp     ssb_exit


ssb_y_extent_ok:
        assumes ds,nothing              ; important!
        assumes es,Data                 ; important!
        mov     loc_y_extent,ax         ; save 0-based height of rectangle

;/*
;**  X-coordinate computations:
;** 
;**  Results will be:
;** 
;**        loc_exclude_x1  = left limit of exclusion rectangle
;**        loc_exclude_x2  = right limit of exclusion rectangle
;**        loc_x_extent    = clipped width of source bits
;** 
;**  The x coords are clipped by taking max(x,0) and
;**  min(x,SCREEN_CX).
;*/

        mov     bx,sdScreen.sd_cx       ; used more than once

        mov     ax,[si].wrc_xRight      ; get x2
        add     ax,loc_x_origin         ; adjust by DC origin
        mov     di,ax
        js      ssb_exit_error_vect     ; invalid rectangle
        smin_ax bx
        sub     di,ax
        mov     loc_clip_right,di       ; amount clipped on right
        dec     ax                      ; make inclusive rectangle
        mov     loc_exclude_x2,ax       ; save for cursor exclusion
        mov     cx,ax                   ; save X2

        mov     ax,[si].wrc_xLeft       ; get x1
        add     ax,loc_x_origin         ; adjust by DC origin
        mov     di,ax
        cwd                             ; create AND mask based on sign(x1)
        not     dx
        and     ax,dx                   ; AX = max(x1,0)
        cmp     ax,bx                   ; is x1 off screen to right?
        jg      ssb_exit_error_vect     ; yes --> invalid rectangle
        neg     di
        add     di,ax
        mov     loc_clip_left,di        ; save amount clipped off left side
        mov     loc_exclude_x1,ax       ; save for cursor exclusion

        sub     cx,ax                   ; compute x extent
        jc      ssb_exit_error_vect     ; if infinitely thin, get out
        mov     loc_x_extent,cx         ; 0-based width of blt

;/*
;**  Now we know that we have a visible portion of the area to save. We can now
;**  validate the cache we are using. This is providing of course that the visible
;**  width and height of the region to be saved does not overstep what we have
;**  available in our save buffer.
;**
;**  Calculate the # of full rectangles and the # of scans in any partial
;**  last rectangle that the area to be saved must be divided up into if it
;**  is to fit into the y extent of our ssb cache buffer. The results obtained
;**  here do not guarantee room in the buffer, but is necessary to the final
;**  results.
;*/

        mov     ax,loc_y_extent         ; get height of area to save
        inc     ax                      ; and make it 1-based
        cwd                             ; zero-extend it too
        div     SsbYExt                 ; area hgt / cache hgt = # rects
        mov     SsbUse.SsbRects,ax      ; # full rects in divided cache
        mov     SsbUse.SsbPartial,dx    ; # of scans in partial last rect

        mov     bx,ax                   ; keep result safe here
        add     dx,-1                   ; figure in any partial last rect
        adc     bx,0

;/*
;**  Determine the # of full rectangles, the width of the input area, that can
;**  be stored in our ssb cache buffer. This value will be the limiting factor
;**  determining whether or not we can save the input area. If this value is
;**  smaller than the sum of the rectangles needed from the y calculation done
;**  above, then we do not have room in the cache to save the input area.
;*/

        mov     ax,SsbXExt              ; total width of our cache buffer
        cwd                             ; zero-extend (guaranteed!)
        inc     cx                      ; 1-based width of area to be saved
        div     cx                      ; / width of area to be saved = # rects
        cmp     ax,bx                   ; do we have room for required # rects ?
        jb      ssb_exit_error_vect     ; our save area is not large enough...

;/*
;** This input area can be saved to our ssb cache buffer. Remember values which
;** must be available when we restore the saved area to the screen.
;*/

        mov     SsbUse.SsbIncr,cx       ; save width of area to be saved
        mov     ax,SsbYExt
        mov     SsbUse.SsbHeight,ax     ; save height of each subrect
        mov     ax,loc_clip_left
        mov     SsbUse.SsbClipLeft,ax   ; remember amount clipped on left
        mov     ax,loc_clip_right
        mov     SsbUse.SsbClipRight,ax  ; remember amount clipped on right
        mov     ax,loc_clip_top
        mov     SsbUse.SsbClipTop,ax    ; remember amount clipped on top
        mov     ax,loc_clip_bottom
        mov     SsbUse.SsbClipBottom,ax ; remember amount clipped on bottom

;/*
;** Before we move any bits, exclude the cursor from the source
;** rectangle.
;*/

        mov     cx,loc_exclude_x1       ; load exclusion rectangle into regs
        mov     dx,loc_exclude_y1
        mov     si,loc_exclude_x2
        mov     di,loc_exclude_y2
        call    far_exclude

        GrabScreen      ssb1            ; don't allow cursor to interfere

        mov     ds,CodeData             ; need data seg addressability
        assumes ds,Data
        assumes es,nothing

;/*
;** Blt the area to be saved to the save screen bits save area. First, initialize
;** hardware registers which will remain constant throughout all full and partial
;** rectangle blts.
;*/

        WaitQ   8
        outwQ   XMIN,(XMIN_2DECODE+0)   ; set full screen clip rectangle
        outwQ   YMIN,(YMIN_2DECODE+0)   ; OK, since we sw clipped
        outwQ   XMAX,(XMAX_2DECODE+1023)
        outwQ   YMAX,(YMAX_2DECODE+1023)
        mov     al,WRITE_ALL_PLANES     ; r/w all planes
        outbQ   WRITE_ENABLE,al         ; write all planes
        outbQ   READ_ENABLE,al          ; read all planes
        .errnz  WRITE_ALL_PLANES - READ_ALL_PLANES
        outwQ   MODE,(MODE_2DECODE+MD_PS_ONES+MD_UP_FALSE)
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S)

;/*
;** For each full subrect blt the source area to be saved to the corresponding
;** area in the cache buffer.
;*/

        WaitQ   2
        outwQ   LX,loc_x_extent         ; set 0-based width of full subrect blt
        mov     ax,SsbYExt              ; set 0-based height of full subrect blt
        dec     ax
        outwQ   LY,ax
        .errnz  LY_2DECODE

        mov     bx,SsbX                 ; starting destination x origin
        mov     si,loc_exclude_x1       ; starting source left side
        mov     di,loc_exclude_y1       ; starting source top side

        mov     cx,SsbUse.SsbRects      ; # of full subrects
        jcxz    ssb_no_full_subrects    ; no full subrects...

@@:     WaitQ   5
        outwQ   X0,si                   ; set source left side
        outwQ   Y0,di                   ; set source top side
        outwQ   X1,bx                   ; set destination left side
        outwQ   Y1,SsbY                 ; set destination top side
        outwQ   CMD_FLAGS,CMD_SSB       ; do the full subrect blt
        
        add     bx,SsbUse.SsbIncr       ; next horizontal band in cache buffer
        add     di,SsbYExt              ; next vertical band in input area
        loop    @B                      ; for all full subrects to blt...

ssb_no_full_subrects:

;/*
;**  All full subrects have been blt'ed, now blt any final partial subrect.
;*/

        mov     cx,SsbUse.SsbPartial    ; any partial scans ?
        jcxz    @F                      ; none...

        WaitQ   6
        dec     cx
        outwQ   LY,cx                   ; height of last blt
        .errnz  LY_2DECODE
        outwQ   X0,si                   ; set source left side
        outwQ   Y0,di                   ; set source top side
        outwQ   X1,bx                   ; set destination left side
        outwQ   Y1,SsbY                 ; set destination top side
        outwQ   CMD_FLAGS,CMD_SSB       ; do the partial subrect blt
@@:

;/*
;** Area has been saved to cache buffer, do our closing chores and exit with
;** a non-zero handle.
;*/

        ReleaseScreen   ssb1            ; cursor allowed to interfere now
        call    far_unexclude           ; put cursor back on screen

        mov     ax,SsbUse.SsbIncr       ; return handle to allow restoring
                                        ; (this is guaranteed to be non-zero)

;/*
;**        AX = return value (0 if error)
;*/

ssb_exit:
        cwd
        call    leave_driver
ssb_exit_no_lock:
        fw_zero <cx,es>
cEnd


;/***************************************************************************
;*
;* FUNCTION NAME = RestoreScreenBits 
;*
;* DESCRIPTION   = 
;*
;*        Restore a rectangle of bits from unused display memory to the display.
;*
;*        Registers Preserved:
;*              SI,DI,DS,BP
;*        Registers Destroyed:
;*              AX,BX,CX,DX,ES,FLAGS
;*        Calls:
;*              leave_driver
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1 if no error  
;* RETURN-ERROR  = AX = 0 if error occured 
;*
;**************************************************************************/

CMD_RSB =       (\
                 CMD_C_CRECT+CMD_FV_FIX+CMD_DX+CMD_DY+CMD_MA_ACCESS+\
                 CMD_PA_FOUR+CMD_RW_W\
                )

rsb_table       label   word
        dw      rsb_free_bits
        dw      rsb_restore_bits
        dw      rsb_restore_bits
        .errnz  RSB_FREE    - 00000001b
        .errnz  RSB_RESTORE - 00000010b

RSB_TABLE_SIZE  = ($-rsb_table)/2


        check   RestoreScreenBits,<hdc,hsb,prcl,flCmd,hddc,ulFunN>

        assumes ds,nothing
        assumes es,nothing

cProc   RestoreScreenBits,<PUBLIC,FAR,NODATA>,<si,di>

        parmD   hdc
        parmD   hSB                     ; handle to saved bits
        parmD   lpRect                  ; destination of rectangle
        parmD   Options                 ; bit 0 = free, bit 1 = restore
        parmD   hddc
        parmD   FunN

        localW  loc_sd_cx               ; width of framebuffer
        localW  loc_sd_cy               ; height of framebuffer

        localW  loc_x_origin            ; DC origin in x
        localW  loc_y_origin            ; DC origin in y

        localV  loc_ssb,%(size SsbCache)

        localW  new_x_clip_lft          ; left clip amount on passed rect
        localW  new_y_clip_top          ; top clip amount on passed rect

        localW  loc_exclude_x1          ; exclusion rectangle corners
        localW  loc_exclude_y1
        localW  loc_exclude_x2
        localW  loc_exclude_y2

cBegin
        fw_zero <ds,es>

        cld
        mov     ds,CodeData
        assumes ds,Data
        mov     dx,hddc.lo
        call    enter_driver
        njc     rsb_exit_no_lock        ; DX:AX = 0 on error

;/*
;** Validate the input handle to ensure that the passed handle is indeed
;** a valid handle for us.
;*/

        mov     cx,hSB.lo               ; fetch the handle
        jcxz    rsb_exit_error_vect_1   ; should never have been 0!

        cmp     cx,SsbUse.SsbIncr       ; we returned the width of each band
        je      @F                      ; valid handle passed to us...

rsb_exit_unknown_function:
rsb_exit_error_vect_1:
        sub     ax,ax                   ; return 0 for error
        jmp     rsb_exit
@@:

;/*
;** Dispatch to the requested subfunction. Just return an error if the passed
;** subfunction is not one that we handle.
;*/

        mov     bx,Options.lo           ; options must be 1-3
        dec     bx                      ; convert to 0-2
        cmp     bx,RSB_TABLE_SIZE       ; must be valid subfunction
        jae     rsb_exit_unknown_function
        shl     bx,1
        jmp     rsb_table[bx]


;/*
;** Subfunctions 2,3 will cause us to restore the saved rectangle back to
;** the destination on the screen.
;*/

rsb_restore_bits:

;/*
;**  Load local frame with things needed locally.
;*/

        mov     si,hddc.lo
        ddc?    si,<SURFACE>
        mov     di,[si].ddc_npsd
        test    [di].sd_fb,SD_DEVICE             ;don't let them restore from bitmap
        jz      rsb_exit_error_vect_1

        les     si,[si].ddc_prddc                ;ES:SI = RDDC
        assumes es,nothing
        rddc?   es,si

        mov     ax,es:[si].rddc_ptsOrg.pts_x
        mov     loc_x_origin,ax
        mov     ax,es:[si].rddc_ptsOrg.pts_y
        mov     loc_y_origin,ax

        mov     ax,sdScreen.sd_cx       ; width of framebuffer
        mov     loc_sd_cx,ax
        mov     ax,sdScreen.sd_cy       ; height of framebuffer
        mov     loc_sd_cy,ax

        mov     si,DataOFFSET SsbUse    ; -> cache slot
        mov     ax,ss
        mov     es,ax
        assumes es,nothing
        lea     di,loc_ssb              ; -> frame locals
        mov     cx,(size SsbCache) shr 1; # words to move
        cld                             ; work up in address
        rep     movsw                   ; move cache struct to local frame

;/*
;**     Load a pointer to the rectangle, and check that the x and
;**     y extents of the given rectangle match those of the one last
;**     saved.  If they don't match, consider it an error and leave.
;**     Clipping of the saved bits can still be performed, if desired,
;**     by selecting a clipping region, which Bitblt will recognize.
;*/

        lds     si,lpRect               ; DS:SI --> rectangle boundaries
        assumes ds,nothing

        mov     ax,[si].wrc_xRight      ; get x2
        sub     ax,[si].wrc_xLeft       ; compute x extent of given rect
ifdef FIREWALLS
        jg      rsb_x_ok
        rip     text,<Rectangle corners misordered>
rsb_x_ok:
endif
        sub     ax,loc_ssb.SsbIncr      ; subtract extent of saved rect
        sub     ax,loc_ssb.SsbClipLeft  ; subtract amount by which saved
        sub     ax,loc_ssb.SsbClipRight ;  rectangle was clipped in x
        jnz     rsb_exit_error_vect_2   ; if not equal, get out

        mov     bx,[si].wrc_yTop        ; get y2
        sub     bx,[si].wrc_yBottom     ; compute y extent of given rect
ifdef FIREWALLS
        jg      rsb_y_ok
        rip     text,<Rectangle corners misordered>
rsb_y_ok:
endif
        mov     ax,loc_ssb.SsbRects     ; get # full subrects in cache buffer
        mul     loc_ssb.SsbHeight       ; AX = # scans in sum of full subrects
        add     ax,loc_ssb.SsbPartial   ; add in any partial scans
        sub     bx,ax                   ; subtract extent of saved rect
        sub     bx,loc_ssb.SsbClipBottom; subtract amount by which saved
        sub     bx,loc_ssb.SsbClipTop   ;  rectangle was clipped in y
        jz      rsb_extents_ok          ; if equal, keep going


;/*
;**        Strategically placed funnel for error conditions.
;*/

rsb_exit_error_vect_2:
        mov     ds,CodeData             ; need good DS for leave_driver
        assumes ds,Data
        sub     ax,ax                   ; return 0 for error
        jmp     rsb_exit

        assumes ds,nothing
rsb_extents_ok:

;/*
;**  Do destination computations first.
;*/

        mov     cx,loc_sd_cy            ; used below
        dec     cx                      ; need as 0-based quantity

        lodsw                           ; get x1
        add     ax,loc_x_origin         ; adjust by DC origin
        mov     bx,ax                   ; save a copy
        cwd                             ; make AND mask based on sign(x1)
        and     ax,dx                   ; AX = min(x1,0)
        neg     ax                      ; AX >= 0
        cmp     ax,loc_ssb.SsbClipLeft  ; at least as much clipped as before?
        jl      rsb_exit_error_vect_2   ; if no, error
        mov     new_x_clip_lft,ax       ; save amt clipped off left
        add     bx,ax                   ; x1 has been fully clipped
        mov     loc_exclude_x1,bx       ; save destination left x

        add     si,2                    ; source is dwords, hi word ignored
        lodsw                           ; get y1
        add     ax,loc_y_origin         ; adjust by DC origin
        mov     bx,ax                   ; save a copy
        cwd
        and     ax,dx
        neg     ax                      ; AX >= 0
        cmp     ax,loc_ssb.SsbClipBottom
        jl      rsb_exit_error_vect_2
        add     bx,ax                   ; y1 has been fully clipped
        neg     bx
        add     bx,cx                   ; y1 flipped to our coordinate system
        mov     loc_exclude_y2,bx       ; save destination bottom y

        add     si,2                    ; source is dwords, hi word ignored
        lodsw                           ; get x2
        add     ax,loc_x_origin         ; adjust by DC origin
        mov     bx,ax                   ; save a copy
        sub     ax,loc_sd_cx
        cwd
        not     dx
        and     ax,dx                   ; AX >= 0
        cmp     ax,loc_ssb.SsbClipRight
        jl      rsb_exit_error_vect_2
        sub     bx,ax                   ; x2 has been fully clipped
        dec     bx                      ; make inclusive rectangle
        mov     loc_exclude_x2,bx       ; save destination right x

        add     si,2                    ; source is dwords, hi word ignored
        lodsw                           ; get y2
        add     ax,loc_y_origin         ; adjust by DC origin
        mov     bx,ax                   ; save a copy
        sub     ax,cx
        dec     ax                      ; screen height in cx was 0-based
        cwd
        not     dx
        and     ax,dx                   ; AX >= 0
        cmp     ax,loc_ssb.SsbClipTop
        jl      rsb_exit_error_vect_2
        mov     new_y_clip_top,ax
        sub     bx,ax                   ; y2 has been fully clipped
        dec     bx                      ; make inclusive rectangle top y
        neg     bx                      ; flip to our coordinate system
        add     bx,cx
        mov     loc_exclude_y1,bx       ; save destination top y

;/*
;**  Now time to exclude any cursor.
;*/

        mov     cx,loc_exclude_x1       ; load exclusion rectangle into regs
        mov     dx,loc_exclude_y1
        mov     si,loc_exclude_x2
        mov     di,loc_exclude_y2
        call    far_exclude
        mov     ds,CodeData             ; recover data segment trashed
        assumes ds,Data

        GrabScreen      ssb2            ; don't let cursor interfere

;/*
;**  Start loading up the hardware registers.
;*/

        WaitQ   8
        outwQ   YMIN,(YMIN_2DECODE+0)   ; we have already clipped to hw bounds
        outwQ   YMAX,(YMAX_2DECODE+1023)
        outwQ   XMIN,(XMIN_2DECODE+0)
        outwQ   XMAX,(XMAX_2DECODE+1023)
        mov     al,WRITE_ALL_PLANES     ; r/w all planes
        outbQ   READ_ENABLE,al
        outbQ   WRITE_ENABLE,al
        .errnz  WRITE_ALL_PLANES - READ_ALL_PLANES
        outwQ   MODE,(MODE_2DECODE+MD_PS_ONES+MD_UP_FALSE)
        outwQ   FUNCTION_1,(FUNC_2OP_COPY+FUNC_S)

;/*
;** For each full subrect blt the saved area in the cache buffer to the
;** corresponding portion of the destination area being restored.
;*/

        WaitQ   4
        mov     ax,loc_ssb.SsbIncr      ; previous width of saved rect
        mov     si,new_x_clip_lft       ; extra clipping is new clipping -
        sub     si,loc_ssb.SsbClipLeft  ;   the older clipping
        sub     ax,si                   ; drop by amt of any extra clipping
        dec     ax                      ; 0-based for hw
        outwQ   LX,ax                   ; send blt width to hw
        mov     ax,SsbYExt
        dec     ax
        outwQ   LY,ax                   ; send blt height to hw
        .errnz  LY_2DECODE
        outwQ   Y0,SsbY                 ; send src top y to adapter (constant)
        outwQ   X1,loc_exclude_x1       ; destination left x (constant)

        mov     bx,SsbX                 ; starting x coord of saved rect
        add     bx,si                   ; add any extra new clipping
        mov     di,loc_ssb.SsbClipTop   ; neg. extra clipping is old clipping -
        sub     di,new_y_clip_top       ;  the new clipping
        add     di,loc_exclude_y1       ; add in new top to give old top

        mov     cx,loc_ssb.SsbRects     ; # of full subrects
        jcxz    rsb_no_full_subrects    ; no full subrects...

@@:     WaitQ   3
        outwQ   X0,bx                   ; send source left x to adapter
        outwQ   Y1,di                   ; destination top y
        outwQ   CMD_FLAGS,CMD_RSB       ; do full subrect blt

        add     bx,loc_ssb.SsbIncr      ; offset to next horizontal band
        add     di,SsbYExt              ; offset to next dest vertical band
        loop    @B                      ; for all full subrects...

rsb_no_full_subrects:

        mov     cx,loc_ssb.SsbPartial   ; any partial band ?
        jcxz    @F                      ; no...

        WaitQ   4
        dec     cx
        outwQ   LY,cx                   ; set new blt height
        .errnz  LY_2DECODE
        outwQ   X0,bx                   ; send source left x to adapter
        outwQ   Y1,di                   ; destination top y
        outwQ   CMD_FLAGS,CMD_RSB       ; do partial subrect blt
@@:

        ReleaseScreen   ssb2            ; allow cursor operations
        call    far_unexclude                         ; allow cursor to show also

        cmp     Options.lo,RSB_RESTORE  ; only restore ?
        je      rsb_exit_ok             ; yes, don't discard...

;/*
;**  Subfunctions 1,3 will cause us to free up the ssb buffer associated with the
;**  saved rectangle.
;*/

rsb_free_bits:

;/*
;**  Invalidate the buffer by stuffing a 0 into the SsbIncr field.
;*/

        mov     SsbUse.SsbIncr,0        ; invalidates cache

rsb_exit_ok:
        mov     ax,1                    ; prepare for "successful" exit

rsb_exit:
        cwd                             ; return DX:AX
        call    leave_driver
rsb_exit_no_lock:
        fw_zero <cx,es>
cEnd

sEnd    Code
        end
