;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = 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:
;*                                                                                    
;*              Only one rectangle of bits can be saved at a time.                    
;*              The hdc passed in must be the screen DC.                              
;*              
;*              
;* FUNCTIONS    SaveScreenBits     
;*              RestoreScreenBits  
;*              ega_scr_copy_bytes
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   05/19/87                     Written by Bob Grudem [bobgru] (Windows),
;*                                Wes Rupel [wesleyr] (PM)
;*   08/06/87                     Bob Grudem [bobgru] Replaced some magic 
;*                                numbers with equates; cleanup style.
;*   09/27/87                     Wesley Rupel [wesleyr] Ported from Windows.
;*                                Total ReWrite.
;*   10/23/87                     Bob Grudem [bobgru] Debugged; now copies 
;*                                to/from specific address in upper EGA mem.
;*   11/05/87                     Bob Grudem [bobgru] Split into two functions 
;*                                according to interface. Added clipping and
;*                                lower-left alignment.
;*   12/09/87                     Bob Grudem [bobgru] Improved register usage.  
;*                                Added lots of comments.
;*   12/31/87                     Walt Moore [waltm] Direct dialing
;*   05/27/88                     Bob Grudem [bobgru] Now saves marker and 
;*                                pattern attribute flags before calling Bitblt,
;*                                clears them for the call, and restores them 
;*                                afterwards.  Also added firewall to check 
;*                                return code from Bitblt.
;*****************************************************************************/

        .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 egamemf.inc
        include egafam.inc
        include njmp.mac
        .list


        externA  SCREEN_CBSCAN
        externA  SCREEN_CX
        externA  SCREEN_CY
        externA  SCREEN_CBRESERVED

        externFP Bitblt
        externFP far_exclude
        externFP far_unexclude
        externFP DosRequestVDD


SSB_COOKIE      equ     'bs'            ;the only allowed hSB
                                        ;(ie, only one client allowed at a time)

sBegin  Code
        externNP enter_driver
        externNP leave_driver
sEnd    Code

sBegin  Data

        externD hVideoVDD
        externB fbOffScreen
        externD pOffScreenUsed
        externD pOffScreenScan
        externW yOffScreenScan
        externW nbOffScreenUsed
        externD lnbTotalScreenSize

;/*
;** The following variables hold information necessary for restoring
;** a rectangle to the display with arbitrary destination and
;** clipping to device boundaries.
;**
;** Since the bits are saved in a fast byte-aligned copy, and always
;** to the same place in upper screen RAM, we have to save the bit
;** alignment within the first byte, and the rectangle dimensions,
;** to know exactly which bits were saved and which are padding.  We save
;** the amount clipped off each edge to determine if we actually have the bits
;** requested by RestoreScreenBits.
;*/

sb_bit_align    db      ?               ;bit alignment of saved bits
sb_x_extent     dw      ?               ;width of saved bits
sb_y_extent     dw      ?               ;height of saved bits
sb_x_clip_lft   dw      ?               ;amt by which rect clipped at left
sb_y_clip_btm   dw      ?               ; . . . . . . . . . . . . . . bottom
sb_x_clip_rgt   dw      ?               ; . . . . . . . . . . . . . . right
sb_y_clip_top   dw      ?               ; . . . . . . . . . . . . . . top

sEnd    Data


sBegin  Code
        assumes cs,Code

        externW CodeData

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


;/***************************************************************************
;*
;* FUNCTION NAME = SaveScreenBits 
;*
;* DESCRIPTION   = 
;*
;*        Save a rectangle of bits to unused display memory.
;*       
;*        Device                        Offset in
;*        coord                         screen RAM
;*        -----------------------------------------
;*        y+    |                       offset 0000
;*              |    +-------------+
;*              |    |             |
;*              |    |             |
;*              |    |      A      |
;*              |    |             |
;*              |    |             |
;*              |....+-------------+
;*              |    .
;*              |    .
;*              |    .
;*        0,0   +======================
;*              +-------------+         offset xxxx
;*              |             |
;*              |             |
;*              |   Saved A   |
;*              |             |
;*              |             |
;*              +-------------+
;*              |
;*              |
;*              |
;*        y-    |                       offset FFFF
;*       
;*       
;*        The rectangle of bits is saved at a fixed location in upper
;*        screen memory.
;*
;*        Registers Preserved:                   
;*              SI,DI,DS,BP                      
;*        Registers Destroyed:                   
;*              AX,BX,CX,DX,ES,FLAGS             
;*
;* INPUT         =   EGA registers in default state 
;*                 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = handle to saved bits   
;* RETURN-ERROR  = AX = 0 if unable to save bits 
;*
;**************************************************************************/


        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_screen_sel          ;screen selector

        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
        localW  loc_y_origin            ; 

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

        cmp     pOffScreenUsed.off,0    ;off-screen memory in use?
        je      ssb_available
        jmp     ssb_exit_error2         ;yes, abort


;/*
;** We will save the bits to off-screen EGA memory by calling
;** ega_src_copy_bytes.  We now set up the arguments for
;** this call.
;**
;** SI will point to the first (lowest addr) byte to be copied.
;** DI will point to the destination.  They are calculated as:
;**
;** SI = (SCREEN_CY-y2-DC_org_y) * SCREEN_CBSCAN + (x1-DC_org_x)/8
;** DI = SSB_SAVE_ADDR
;*/

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

        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,[di].sd_pBits.sel    ;get screen selector
        mov     loc_screen_sel,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
;** sb_y_extent    = number of scan lines to copy
;**
;** DI = offset of first byte of first source scan line
;**
;** 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 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,SCREEN_CY            ;used more than once

        mov     ax,[si].wrc_yTop        ;get y2
        add     ax,loc_y_origin         ;adjust by DC origin
        js      ssb_exit_error          ;invalid rectangle
        neg     ax                      ;flip to screen coords
        add     ax,bx                   ; 
        cwd                             ;y2 <-- max(y2,0)
        mov     di,ax                   ;save y2 in DI
        and     ax,dx                   ;AX = min(y2,0) = - amt clipped
        neg     ax                      ;make it positive
        mov     sb_y_clip_top,ax        ;save amt of clipping
        xchg    ax,di                   ;bring back y2
        not     dx                      ; 
        and     ax,dx                   ;AX = max(y2,0)
        mov     loc_exclude_y1,ax       ;save for cursor exclusion
        xchg    ax,cx                   ;store in CX for extent calculation

        mov     ax,[si].wrc_yBottom     ;get y1
        add     ax,loc_y_origin         ;adjust by DC origin
        cwd                             ;create AND mask based on sign(y1)
        mov     di,ax                   ;save y1 in DI
        and     ax,dx                   ;AX = min(y1,0) = - amt clipped
        neg     ax                      ;make it positive
        mov     sb_y_clip_btm,ax        ;save amt of clipping
        xchg    ax,di                   ;bring back y1
        not     dx                      ;do the clipping
        and     ax,dx                   ;AX = max(y1,0)
        neg     ax                      ;flip to screen coords
        add     ax,bx                   ; 
        js      ssb_exit_error          ;invalid rectangle
        mov     loc_exclude_y2,ax       ;save for cursor exclusion

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

ssb_exit_error:
        mov     ds,CodeData             ;restore DS for leave_driver
        assumes ds,Data
ssb_exit_error2:
        xor     ax,ax                   ;return 0 for error
        jmp     ssb_exit                ;ownership released

ssb_y_extent_ok:
        assumes ds,nothing              ;important!
        assumes es,Data                 ;important!
        mov     sb_y_extent,ax          ;save it for RestoreScreenBits
        imul    di,cx,SCREEN_CBSCAN     ;put starting scan address in DI

;/*
;** X-coordinate computations:
;**
;** Results will be:
;**
;** loc_exclude_x1 =  left limit of exclusion rectangle
;** loc_exclude_x2 = right limit of exclusion rectangle
;** sb_x_extent    = original width of source bits (excluding padding)
;** sb_bit_align   = original alignment of first source bit within byte
;**
;** SI = offset of first byte to transfer from source
;**
;** The x coords are clipped by taking max(x,0) and
;** min(x,SCREEN_CX).
;*/

        mov     bx,SCREEN_CX            ;used more than once
        mov     ax,[si].wrc_xRight      ;get x2
        add     ax,loc_x_origin         ;adjust by DC origin
        js      ssb_exit_error          ;invalid rectangle
        mov     cx,ax                   ;save AX in CX
        sub     ax,bx                   ;AX > 0 if right edge clipped
        neg     ax                      ;AX < 0 if ...
        cwd                             ;DX = FFFF if ...
        and     ax,dx                   ;AX = - amount clipped
        neg     ax                      ;make it positive
        mov     sb_x_clip_rgt,ax        ;store amt clipped
        xchg    ax,cx                   ;restore x2 to AX
        smin_ax bx              ; 
        mov     loc_exclude_x2,ax       ;save for cursor exclusion
        xchg    ax,cx                   ;save for byte address calculations

        mov     ax,[si].wrc_xLeft       ;get x1
        add     ax,loc_x_origin         ;adjust by DC origin
        cwd                             ;create AND mask based on sign(x1)
        mov     si,ax                   ;save x1 in SI
        and     ax,dx                   ;AX = min(x1,0) = amt clipped
        neg     ax                      ;make it positive
        mov     sb_x_clip_lft,ax        ;save amt clipped
        xchg    ax,si                   ;get x1 back in AX
        not     dx                      ; 
        and     ax,dx                   ;AX = max(x1,0)
        cmp     ax,bx                   ;is x1 off screen to right?
        jg      ssb_exit_error          ;yes --> invalid rectangle
        mov     loc_exclude_x1,ax       ;save for cursor exclusion

        mov     si,cx                   ;make a copy of x2
        sub     si,ax                   ;compute x extent
        jz      ssb_exit_error          ;if infinitely thin, get out
        mov     sb_x_extent,si          ;save it for RestoreScreenBits

        mov     bx,ax                   ;save x1 for sb_bit_align
        shr     ax,3                    ;compute byte address within scan
        shr     cx,3                    ;compute byte address within scan
        sub     cx,ax                   ;compute # bytes to copy
        inc     cx                      ;  per scan
        mov     si,di                   ;get address of starting scan
        add     si,ax                   ;get address of starting byte

        and     bl,00000111b            ;compute bit alignment of rectangle
        mov     sb_bit_align,bl         ;save for RestoreScreenBits

        mov     dx,sb_y_extent          ;get number of scans to copy

;/*
;** Now that we know how many bytes of off-screen memory we need
;** (ie, up to (DX+1)*SCREEN_CBSCAN-1), it's time to make sure we have
;** (or can allocate) that much.  Note that substantial off-screen memory
;** can be wasted, because the computation just given assumes the worst
;** case (ie, that the memory we have available will start at some
;** scanline+1, forcing us to waste SCREEN_CBSCAN-1 bytes just to start
;** the copy on a scanline boundary), and because the rectangle we were
;** given may be substantially thinner than a scanline.  But I don't
;** intend to fix those problems now.  [jeffpar]
;**
;** First, we check to see if the Video VDD is loaded; if not, then
;** all the memory from SCREEN_CBRESERVED on up is ours to play with....
;*/

        push    dx
        mov     ax,SCREEN_CBSCAN
        mul     dx
        add     ax,SCREEN_CBSCAN
        dec     ax                      ;AX == # bytes needed (worst case)

        test    fbOffScreen,OFFSCR_VDD  ;Video VDD loaded?
        jz      ssb_no_vdd              ;nope

        mov     bx,ax                   ;save size in BX
        sub     dx,dx
        farPtr  hvdd,hVideoVDD.hi,hVideoVDD.lo
        farPtr  cbInput,dx,dx           ;NOWAIT request-type
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,ax
        farPtr  pOutput,es,<DataOFFSET pOffScreenUsed>
        cCall   DosRequestVDD,<hvdd,dx,VVDSYSREQ_REQMEM,cbInput,pInput,cbOutput,pOutput>
        mov     di,pOffScreenUsed.off
        or      ax,ax                   ;did the VDD return an error?
        jnz     ssb_no_memory           ;yes
        mov     ax,bx                   ;recover size from BX
        jmp     short ssb_got_memory

ssb_no_vdd:
        mov     di,SCREEN_CBRESERVED
        mov     bx,lnbTotalScreenSize.lo
        sub     bx,di
        cmp     ax,bx                   ;enough off-screen memory?
        jbe     ssb_got_memory

ssb_no_memory:
        pop     dx
        jmp     ssb_exit_error          ;nope

ssb_got_memory:
        mov     pOffScreenUsed.off,di   ;record off-screen memory address
        mov     nbOffScreenUsed,ax      ;and size

        mov     ax,di                   ;adjust DI to first scanline boundary
        sub     dx,dx
        mov     bx,SCREEN_CBSCAN
        div     bx                      ;DX == remainder
        or      dx,dx
        jz      ssb_no_adjust
        sub     di,dx
        add     di,SCREEN_CBSCAN        ;adjust by (SCREEN_CBSCAN - remainder)
        inc     ax

ssb_no_adjust:
        pop     dx
        mov     pOffScreenScan.off,di   ;record starting scanline address, too
        mov     yOffScreenScan,ax       ;also save the y-coordinate of the scanline

;/*
;** Currently:
;**
;** CX = inner loop count (number of bytes per scan line)
;** DX = outer loop count (number of scan lines)
;** ES:SI --> source bits, padded for byte alignment
;**
;** Before we move any bits, exclude the cursor from the source
;** rectangle.
;*/

        mov     es,loc_screen_sel
        assumes es,nothing

        pushem  cx,dx,si,di,es          ;!!!exclude can trash ES
        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
        inc     si                      ;make SI (right)  inclusive
        inc     di                      ;make DI (bottom) inclusive
        call    far_exclude
        popem   cx,dx,si,di,es

;/*
;** At the call to ega_src_copy_bytes, the registers will contain:
;**
;** CX = inner loop count (number of bytes per scan line)
;** DX = outer loop count (number of scan lines)
;** ES:SI --> source bits, padded for byte alignment
;** ES:DI --> destination of bits
;*/

        call    ega_src_copy_bytes      ;save the bits
        assumes ds,nothing              ;subroutine blows away DS

        call    far_unexclude           ;put cursor back on screen

        mov     ds,CodeData
        assumes ds,Data

        mov     ax,SSB_COOKIE           ;return handle to allow restoring
        and     fbOffScreen,NOT OFFSCR_TRASHED

;/*
;** 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.
;*       
;*        Device                        Offset in
;*        coord                         screen RAM
;*        -----------------------------------------
;*        y+    |                       offset 0000
;*              |    +-------------+
;*              |    |             |
;*              |    + - - - - - + |
;*              |    |             |
;*              |    |           | |
;*              |    |             |
;*              |....+-----------+-+
;*              |    .
;*              |    .
;*              |    .
;*        0,0   +======================
;*              +-------------+         offset xxxx
;*              |             |
;*              + - - - - - + |
;*              |             |
;*              |           | |
;*              |             |
;*            A +-----------+-+
;*              |
;*              |
;*              |
;*        y-    |                       offset FFFF
;*       
;*       
;*        The source (x,y) is always at "A" in the diagram above.  The clipping is
;*        performed by adjusting the upper right corner of the destination
;*        rectangle, so that the saved bits appear flush with the lower left.
;*       
;*        Note:  If the saved bits were clipped to the display borders, then the
;*               caller must restore in such a way that RestoreScreenBits can put
;*               up all the bits, or it will return an error.
;*                 
;*               Registers Preserved:                 
;*                     SI,DI,DS,BP                    
;*               Registers Destroyed:                 
;*                     AX,BX,CX,DX,ES,FLAGS           
;*
;* INPUT         = EGA registers in default state  
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1 if no error  
;* RETURN-ERROR  = AX = 0 if error occured 
;*
;**************************************************************************/

        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

        localV  Points,<(3 * (SIZE RECTL) / 2)> ;bitblt arg

        localW  loc_x_origin            ;DC origin
        localW  loc_y_origin            ; 

        localW  loc_x_extent            ;local copy of x extent of saved bits
        localW  loc_y_extent            ;same, for y extent
        localB  loc_bit_align           ;same, for bit alignment
        localW  loc_x_clip_lft
        localW  loc_y_clip_btm
        localW  loc_x_clip_rgt
        localW  loc_y_clip_top
        localW  loc_y_offscreen

        localW  new_x_extent
        localW  new_y_extent
        localW  new_x_clip_lft
        localW  new_y_clip_btm
        localW  new_x_clip_rgt
        localW  new_y_clip_top

        localB  saved_iflags            ;ddc_ia.ia_ba.ba_fb
        localB  saved_pflags            ;ddc_pa.pa_ba.ba_fb

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

        cmp     hSB.lo,SSB_COOKIE       ;validate handle
        jnz     rsb_exit_error_vect_1

        cmp     pOffScreenUsed.off,0    ;is any off-screen memory in use?
        je      rsb_exit_error_vect_1   ;nope

        mov     bx,Options.lo
        and     bx,3                    ;options must be 1-3
        dec     bx                      ;convert to 0-2
        shl     bx,1
        jmp     rsb_table[bx]

;/*
;** We will call Bitblt to copy the bits.  The Mix is "source copy" (S).
;** Both DC's are the ScreenDC.  The point (x1,y1) is the destination,
;** and (x2,y2) is the source, where x2 = 0 (adjusted for correct bit
;** alignment), and y2 = yOffScreenScan.  We must clip the saved bits to
;** the destination rectangle.
;*/

rsb_restore_bits:
        test    fbOffScreen,OFFSCR_TRASHED
        jz      rsb_restore_1           ;memory contents are OK

rsb_exit_error_vect_1:
        xor     ax,ax                   ;return 0 for error
        jmp     rsb_exit

;/*
;** Load DC origin into local stack frame.
;*/

rsb_restore_1:
        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

        mov     al,sb_bit_align         ;make local copies for access
        mov     loc_bit_align,al        ;  while DS and ES are unavailable
        mov     ax,sb_x_extent
        mov     loc_x_extent,ax
        mov     ax,sb_y_extent
        mov     loc_y_extent,ax
        mov     ax,sb_x_clip_lft
        mov     loc_x_clip_lft,ax
        mov     ax,sb_y_clip_btm
        mov     loc_y_clip_btm,ax
        mov     ax,sb_x_clip_rgt
        mov     loc_x_clip_rgt,ax
        mov     ax,sb_y_clip_top
        mov     loc_y_clip_top,ax
        mov     ax,yOffScreenScan
        mov     loc_y_offscreen,ax

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

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

;/*
;** 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_x_extent         ;subtract extent of saved rect
        sub     ax,loc_x_clip_lft       ;subtract amount by which saved
        sub     ax,loc_x_clip_rgt       ;  rectangle was clipped in x
        jnz     rsb_exit_error_vect_2   ;if not equal, get out

        mov     ax,[si].wrc_yTop        ;get y2
        sub     ax,[si].wrc_yBottom     ;compute y extent of given rect
ifdef FIREWALLS
        jg      rsb_y_ok
        rip     text,<Rectangle corners misordered>
rsb_y_ok:
endif
        sub     ax,loc_y_extent         ;subtract extent of saved rect
        sub     ax,loc_y_clip_btm       ;subtract amount by which saved
        sub     ax,loc_y_clip_top       ;  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
        xor     ax,ax                   ;return 0 for error
        jmp     rsb_exit


        assumes ds,nothing
rsb_extents_ok:
        push    ss                      ;ES:DI --> Points struct for blt
        pop     es
        assumes es,nothing
        lea     di,Points
        .errnz  wrc_xLeft

;/*
;** Copy lpRect into Points.  All high words of coordinates are 0000h.
;**
;** Do destination computations first:
;*/

        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
        mov     new_x_clip_lft,ax       ;save amt clipped off left
        add     bx,ax                   ;x1 has been fully clipped
        cmp     ax,loc_x_clip_lft       ;at least as much clipped as before?
        jl      rsb_exit_error_vect_2   ;if no, error
        xchg    ax,bx                   ;put x1 back into AX
        stosw                           ;store in Points
        xor     ax,ax
        stosw                           ;0
        inc     si                      ;increment past hi word
        inc     si                      ; or source rectangle
        .errnz  wrc_xLeft

        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
        mov     new_y_clip_btm,ax
        add     bx,ax                   ;y1 has been fully clipped
        cmp     ax,loc_y_clip_btm
        jl      rsb_exit_error_vect_2
        xchg    ax,bx                   ;put y1 back into AX
        stosw                           ;store in Points
        xor     ax,ax
        stosw                           ;0
        inc     si                      ;increment past hi word
        inc     si                      ; or source rectangle
        .errnz  wrc_yBottom - wrc_xLeft - 4

        lodsw                           ;get x2
        add     ax,loc_x_origin         ;adjust by DC origin
        mov     bx,ax                   ;save a copy
        sub     ax,SCREEN_CX
        cwd
        not     dx
        and     ax,dx                   ;AX >= 0
        mov     new_x_clip_rgt,ax
        sub     bx,ax                   ;x2 has been fully clipped
        cmp     ax,loc_x_clip_rgt
        jl      rsb_exit_error_vect_2
        xchg    ax,bx                   ;put x2 back into AX
        stosw                           ;store in Points
        xor     ax,ax
        stosw                           ;0
        inc     si                      ;increment past hi word
        inc     si                      ; or source rectangle
        .errnz  wrc_xRight - wrc_yBottom - 4

        lodsw                           ;get y2
        add     ax,loc_y_origin         ;adjust by DC origin
        mov     bx,ax                   ;save a copy
        sub     ax,SCREEN_CY
        cwd
        not     dx
        and     ax,dx                   ;AX >= 0
        mov     new_y_clip_top,ax
        sub     bx,ax                   ;y2 has been fully clipped
        cmp     ax,loc_y_clip_top
        jge     clip_test_ok            ; DAK 21/NOV/90 was jl reb_exit_error_vect_2
        jmp     rsb_exit_error_vect_2
clip_test_ok:
        xchg    ax,bx                   ;put y2 back into AX
        stosw                           ;store in Points
        xor     ax,ax
        stosw                           ;0
        inc     si                      ;increment past hi word
        inc     si                      ; or source rectangle
        .errnz  wrc_yTop - wrc_xRight - 4

;/*
;** Do source computations:
;**
;** The first three general-case lines are optimized into the
;** following two:
;*/

        mov     al,loc_bit_align        ;loc_bit_align is in {0,...,7}
        cbw

        mov     bx,new_x_clip_lft       ;find out how much extra clipping
        sub     bx,loc_x_clip_lft       ;known to be non-negative
        add     ax,bx                   ;add in the clipping
        cwd
        stosw
        xchg    ax,dx
        stosw
        .errnz  wrc_xLeft

        mov     ax,SCREEN_CY
        sub     ax,loc_y_offscreen      ;highest y coord in source
        sub     ax,loc_y_extent         ;lowest y coord in source
        mov     bx,new_y_clip_btm       ;get total lines clipped
        sub     bx,loc_y_clip_btm       ;get extra lines clipped
        add     ax,bx                   ;add in extra clipping
        cwd
        stosw
        xchg    ax,dx
        stosw
        .errnz  wrc_yBottom - wrc_xLeft - 4

;/*
;** Load registers and make the call to restore the bits.
;** Since our source is in the screen DC, we must disable input
;** clipping in bitblt.  This is accomplished by setting a bit
;** in the DDC which bitblt checks.
;*/

        mov     ds,CodeData             ;disable input clipping in bitblt
        assumes ds,Data
        mov     si,hddc.lo
        or      [si].ddc_fb,DDC_SSB_CALL

;/*
;** Save the image and pattern basic attribute flags, which
;** could cause an attempt at color realization inside Bitblt
;** which we don't want.  Clear them before calling down.
;*/

        mov     al,[si].ddc_ia.ia_ba.ba_fb
        mov     saved_iflags,al
        mov     al,[si].ddc_pa.pa_ba.ba_fb
        mov     saved_pflags,al
        xor     ax,ax                   ;generic null parameter
        and     [si].ddc_ia.ia_ba.ba_fb,al
        and     [si].ddc_pa.pa_ba.ba_fb,al

        check   Bitblt,<hdc,hdcSrc,cptl,pbbp,lMix,flCmd,pbba,hddc,ulFunN>

        lea     bx,Points               ;part of lpPts
        mov     si,FunN.hi              ;part of MyFunN
        and     si,COM_DRAW             ;no correlation, bounds, xform, etc.
        farPtr  mix,<ax>,<00CCh>        ;ROP = Source Copy
        farPtr  Cnt,<ax>,<3>
        farPtr  lpAttrs,<ax>,<ax>
        farPtr  Styl,<ax>,<ax>
        farPtr  lpPts,<ss>,<bx>
        .errnz  wrc_xLeft
        farPtr  MyFunN,<si>,<off_Bitblt>
        cCall   Bitblt,<hdc,hdc,Cnt,lpPts,mix,Styl,lpAttrs,hddc,MyFunN>

ifdef FIREWALLS
;/*
;** We're supposed to ensure that Bitblt doesn't falter.  If it does,
;** it's our problem.
;*/
        or      ax,ax
        jnz     @F
        rip     text,<Bitblt error in RestoreScreenBits>
@@:
endif
        mov     si,hddc.lo              ;assuming DS = Data
        and     [si].ddc_fb,not DDC_SSB_CALL

;/*
;** Restore original values of basic attribute flags.  The
;** next caller who needs colors might return an error if
;** something's invalid, but at least we won't.
;*/

        mov     al,saved_iflags
        mov     [si].ddc_ia.ia_ba.ba_fb,al
        mov     al,saved_pflags
        mov     [si].ddc_pa.pa_ba.ba_fb,al

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

rsb_exit:
        test    Options.lo,RSB_FREE     ;free also?
        jz      rsb_really_exit         ;no, so don't discard

        sub     dx,dx
        test    fbOffScreen,OFFSCR_VDD  ;Video VDD loaded?
        jz      rsb_no_vdd              ;nope

        push    ax                      ;save return code
        farPtr  hvdd,hVideoVDD.hi,hVideoVDD.lo
        farPtr  cbInput,dx,dx
        farPtr  pInput,dx,dx
        farPtr  cbOutput,dx,nbOffScreenUsed
        farPtr  pOutput,ds,<DataOFFSET pOffScreenUsed>
        cCall   DosRequestVDD,<hvdd,dx,VVDSYSREQ_FREEMEM,cbInput,pInput,cbOutput,pOutput>
        pop     ax                      ;restore return code

rsb_no_vdd:
        mov     pOffScreenUsed.off,dx   ;zero all these fields now
        mov     pOffScreenScan.off,dx
        mov     yOffScreenScan,dx
        mov     nbOffScreenUsed,dx
        and     fbOffScreen,NOT OFFSCR_TRASHED

rsb_really_exit:
        cwd                             ;return DX:AX
        call    leave_driver

rsb_exit_no_lock:
        fw_zero <cx,es>
cEnd


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ega_src_copy_bytes  
;*
;* DESCRIPTION   =  This is essentially a special case of the more general     
;*                  ega_src_copy routine found in Bitblt.  As a special case it
;*                  is also more general in that it can be called              
;*                  out-of-context, whereas ega_src_copy depends on various    
;*                  bitblt parameters.  This is also faster because we know    
;*                  ahead of time that the copy is byte aligned.               
;*                                                                             
;*                  Note:                                                            
;*                  This copies in the forward direction only (i.e. CLD is done)
;*                  thus you can get into trouble if you try to use this for
;*                  certain overlapping rectangles.
;*                                                                             
;*                  Registers Destroyed:                                                                        
;*                        AX,BX,CX,DX,SI,DI,DS,flags                                                            
;*                  Registers Preserved:                                                                        
;*                        BP                                                                                    
;*                                                                             
;* INPUT         =  EGA registers in default state                     
;*                  ES = ScreenSelector                                
;*                  DX = Y extent (number of scanlines)                
;*                  CX = inner_loop_count (bytes per scanline to copy) 
;*                  SI = initial source byte                           
;*                  DI = initial dest byte                             
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  ega_src_copy_bytes

ega_src_copy_bytes proc near
        jcxz    escb_nobytes
        cld
        push    dx
        mov     ax,es
        mov     ds,ax
        assumes ds,nothing
        mov     bx,SCREEN_CBSCAN
        sub     bx,cx                      ; BX = wrap (bytes to add to next
                                           ;   scan = 80 - CX)
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0000h + GRAF_BIT_MASK   ; mask out all bits (use latches)
        out16   dx,ax

        pop     ax
        mov     dx,cx                   ; save inner_loop_count
escb_next_scanline:
        rep     movsb
        add     si,bx                   ; --> next source
        add     di,bx                   ; --> next destination
        mov     cx,dx                   ; restore inner_loop_count
        dec     ax                      ; was that the last scan?
        jnz     escb_next_scanline

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out16   dx,ax

escb_nobytes:
        ret
ega_src_copy_bytes endp

sEnd    Code
        end
