;*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
;*
;*
;* 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.
;*   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.
;*   12/09/87                     Bob Grudem [bobgru] Improved register usage.
;*                                Added lots of comments.
;*   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.
;*   11/26/92                     John Batty. Added DCAF changes.
;*
;*****************************************************************************/

        .386

        .xlist

INCL_GRE_BITMAPS        equ     1
INCL_DDICOMFLAGS        equ     1
INCL_DOSMVDM            equ     1
        include pmgre.inc

DINCL_SAVE_SCREEN_BITS  equ     1
DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1

        include driver.inc
        include display.inc
        include egafam.inc
        include extern.inc
        include protos.inc
if SCAN_CNT EQ 768
        include oem_macs.inc
endif
        .list

        .MODEL FLAT

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

        .DATA

SSB_COOKIE      equ     'bs'            ;the only allowed hSB

;/*
;** 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    BYTE    ?               ;bit alignment of saved bits
sb_x_extent     DWORD   ?               ;width of saved bits
sb_y_extent     DWORD   ?               ;height of saved bits
sb_x_clip_lft   DWORD   ?               ;amt by which rect clipped at left
sb_y_clip_btm   DWORD   ?               ; . . . . . . . . . . . . . . bottom
sb_x_clip_rgt   DWORD   ?               ; . . . . . . . . . . . . . . right
sb_y_clip_top   DWORD   ?               ; . . . . . . . . . . . . . . top

rsb_table       label   DWORD

        DWORD   rsb_free_bits
        DWORD   rsb_restore_bits
        DWORD   rsb_restore_bits

        .errnz  RSB_FREE    - 00000001b

        .errnz  RSB_RESTORE - 00000010b

RSB_TABLE_SIZE  = ($-rsb_table)/2

        .CODE

;/***************************************************************************
;*
;* 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
;*
;**************************************************************************/

ALIGN 4
SaveScreenBits  PROC SYSCALL USES ebx esi edi,      hdc:DWORD, lpRect:DWORD,
                                                hddc:DWORD, FunN:DWORD
                LOCAL   loc_screen_sel:DWORD,
                        loc_exclude_x1:DWORD,
                        loc_exclude_y1:DWORD,
                        loc_exclude_x2:DWORD
                LOCAL   loc_exclude_y2:DWORD,
                        loc_x_origin:DWORD,
                        loc_y_origin:DWORD

        DebugMsg <SaveScreenBits, DISPATCH, ssb, CLIFFL>

        cld
        mov     edx,hddc
        call    enter_driver
        jc      ssb_exit_no_lock        ;DX:AX = 0 on error

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


;/*
;** 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     esi,hddc              ;load DC origin into local frame

        ddc?    esi

        ASSUME  esi:PTR DDC, edi:PTR SURFACE

        mov     edi,[esi].ddc_npsd
        test    [edi].sd_fb,SD_DEVICE
        jz      ssb_exit_error2         ;don't let them save from a bitmap!
        mov     esi,[esi].ddc_prddc       ;ES:SI = RDDC

        rddc?   esi

        ASSUME  esi:PTR RDDC

        mov     eax,[esi].rddc_ptsOrg.ptl_x
        mov     loc_x_origin,eax
        mov     eax,[esi].rddc_ptsOrg.ptl_y
        mov     loc_y_origin,eax

        mov     esi,lpRect               ;DS:SI --> rectangle boundaries

        ASSUME  esi:PTR RECTL

;/*
;** 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     eax,[esi].rcl_yTop
        sub     eax,[esi].rcl_yBottom
        jl      ssb_no_way_jose
        mov     eax,[esi].rcl_xRight
        sub     eax,[esi].rcl_xLeft
        jge     ssb_get_on_with_it

ssb_no_way_jose:

        rip     text,<Rectangle corners misordered>

ssb_get_on_with_it:

endif

        mov     ebx,SCREEN_CY            ;used more than once

        mov     eax,[esi].rcl_yTop        ;get y2
        add     eax,loc_y_origin         ;adjust by DC origin
        movsx   eax,ax
        js      ssb_exit_error          ;invalid rectangle
        neg     eax                      ;flip to screen coords
        add     eax,ebx                   ; 
        cdq                               ;y2 <-- max(y2,0)
        mov     edi,eax                   ;save y2 in DI
        and     eax,edx                   ;AX = min(y2,0) = - amt clipped
        neg     eax                      ;make it positive
        mov     sb_y_clip_top,eax        ;save amt of clipping
        xchg    eax,edi                   ;bring back y2
        not     edx                      ; 
        and     eax,edx                   ;AX = max(y2,0)
        mov     loc_exclude_y1,eax       ;save for cursor exclusion
        xchg    eax,ecx                   ;store in CX for extent calculation

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

        sub     eax,ecx                   ;compute y extent
        jnz     ssb_y_extent_ok         ;if infinitely thin, get out

ssb_exit_error:

ssb_exit_error2:

        xor     eax,eax                   ;return 0 for error
        jmp     ssb_exit                ;ownership released
ALIGN 4

ssb_y_extent_ok:

        mov     sb_y_extent,eax          ;save it for RestoreScreenBits
        imul    edi,ecx,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     ebx,SCREEN_CX            ;used more than once
        mov     eax,[esi].rcl_xRight      ;get x2
        add     eax,loc_x_origin         ;adjust by DC origin
        movsx   EAX,AX
        js      ssb_exit_error          ;invalid rectangle
        mov     ecx,eax                   ;save AX in CX
        sub     eax,ebx                   ;AX > 0 if right edge clipped
        neg     eax                      ;AX < 0 if ...
        cdq                              ;DX = FFFF if ...
        and     eax,edx                   ;AX = - amount clipped
        neg     eax                      ;make it positive
        mov     sb_x_clip_rgt,eax        ;store amt clipped
        xchg    eax,ecx                   ;restore x2 to AX
        smin_ax ebx              ; 
        mov     loc_exclude_x2,eax       ;save for cursor exclusion
        xchg    eax,ecx                   ;save for byte address calculations

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

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

        mov     ebx,eax                   ;save x1 for sb_bit_align
        shr     eax,3                    ;compute byte address within scan
        shr     ecx,3                    ;compute byte address within scan
        sub     ecx,eax                   ;compute # bytes to copy
        inc     ecx                      ;  per scan
        mov     esi,edi                   ;get address of starting scan
        add     esi,eax                   ;get address of starting byte

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

        mov     edx,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    edx
        mov     eax,SCREEN_CBSCAN
        mul     edx
        add     eax,SCREEN_CBSCAN
        dec     eax                      ;AX == # bytes needed (worst case)

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

        mov     ebx,eax                   ;save size in BX

        INVOKE  DosRequestVDD, hVideoVDD, 0, VVDSYSREQ_REQMEM, 0, 0, eax,
                                ADDR pOffScreenUsed

        mov     edi,pOffScreenUsed
        or      eax,eax                   ;did the VDD return an error?
        jnz     ssb_no_memory           ;yes
        mov     eax,ebx                   ;recover size from BX
        jmp     ssb_got_memory
ALIGN 4

ssb_no_vdd:

        mov     edi,SCREEN_CBRESERVED
        mov     ebx,lnbTotalScreenSize    ; 
        shr     ebx,2                     ; /4 to get per plane
        cmp     ebx,010000h               ; 
        jbe     @F
        mov     ebx,010000h               ;64k Max per bank !!!
@@:
        sub     ebx,edi
        cmp     eax,ebx                   ;enough off-screen memory?
        jbe     ssb_got_memory

ssb_no_memory:

        pop     edx
        jmp     ssb_exit_error          ;nope
ALIGN 4

ssb_got_memory:

        mov     pOffScreenUsed,edi      ;record off-screen memory address
        mov     nbOffScreenUsed,eax     ;and size

        mov     eax,edi                   ;adjust DI to first scanline boundary
        sub     edx,edx
        mov     ebx,SCREEN_CBSCAN
        div     ebx                      ;DX == remainder
        or      edx,edx
        jz      ssb_no_adjust
        sub     edi,edx
        add     edi,SCREEN_CBSCAN        ;adjust by (SCREEN_CBSCAN - remainder)
        inc     eax

ssb_no_adjust:

        pop     edx
        mov     pOffScreenScan,edi   ;record starting scanline address, too
if SCAN_CNT EQ 768
        add     eax,SCREEN_LPB
endif
        mov     yOffScreenScan,eax       ;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.
;*/


        pushad                           ;!!!exclude can trash ES
        mov     ecx,loc_exclude_x1       ;load exclusion rectangle into regs
        mov     edx,loc_exclude_y1
        mov     esi,loc_exclude_x2
        mov     edi,loc_exclude_y2
        inc     esi                      ;make SI (right)  inclusive
        inc     edi                      ;make DI (bottom) inclusive
        call    far_exclude
        popad

;/*
;** 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
;**
;*/

if SCAN_CNT EQ 768
PUBLIC do_banks
do_banks::
        PUSH    EDX
        mov     dl,1
        SetBankW
        POP     EDX

        ADD     ESI,pVRAMInstance
        ADD     EDI,pVRAMInstance
        cmp     loc_exclude_y1,SCREEN_LPB
        jae     all_bank1
        ;do bank0
        cmp     loc_exclude_y2,SCREEN_LPB
        jb      all_bank0
        ;both banks
both_banks:
        mov     dl,0
        SetBankR

        mov     edx,SCREEN_LPB
        sub     edx,loc_exclude_y1

        call    ega_src_copy_bytes      ;save the bits

        sub     esi,010000h

        mov     dl,1
        SetBankR

        mov     edx,loc_exclude_y2
        sub     edx,SCREEN_LPB

        call    ega_src_copy_bytes      ;save the bits

        jmp     done
ALIGN 4
all_bank0:
        PUSH    EDX
        mov     dl,0
        SetBankR
        POP     EDX

        call    ega_src_copy_bytes      ;save the bits
        jmp     done
ALIGN 4
all_bank1:
        sub     esi,010000h

        PUSH    EDX
        mov     dl,1
        SetBankR
        POP     EDX

        call    ega_src_copy_bytes      ;save the bits
done:

else ;768
        ADD     ESI,pVRAMInstance
        ADD     EDI,pVRAMInstance
        call    ega_src_copy_bytes      ;save the bits
endif ;768


        call    far_unexclude            ;put cursor back on screen

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

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

ssb_exit:

        call    leave_driver

ssb_exit_no_lock:

        fw_zero <ecx>

                RET

SaveScreenBits  ENDP

;/***************************************************************************
;*
;* 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
;*
;**************************************************************************/

ALIGN 4
RestoreScreenBits       PROC SYSCALL USES ebx esi edi,  hdc:DWORD, hSB:DWORD,
                                                        lpRect:DWORD, Options:DWORD,
                                                        hddc:DWORD, FunN:DWORD
                        LOCAL   Points[3]:POINTL,
                                loc_x_origin:DWORD,
                                loc_y_origin:DWORD,
                                loc_x_extent:DWORD,
                                loc_y_extent:DWORD,
                                loc_x_clip_lft:DWORD
                        LOCAL   loc_y_clip_btm:DWORD,
                                loc_x_clip_rgt:DWORD,
                                loc_y_clip_top:DWORD,
                                loc_y_offscreen:DWORD,
                                new_x_extent:DWORD
                        LOCAL   new_y_extent:DWORD,
                                new_x_clip_lft:DWORD,
                                new_y_clip_btm:DWORD,
                                new_x_clip_rgt:DWORD,
                                new_y_clip_top:DWORD,
                                saved_iflags:BYTE,
                                saved_pflags:BYTE,
                                loc_bit_align:BYTE

        DebugMsg <RestoreScreenBits, DISPATCH, ssb, CLIFFL>

        cld
        mov     edx,hddc
        call    enter_driver
        jc      rsb_exit_no_lock        ;DX:AX = 0 on error

        cmp     hSB,SSB_COOKIE       ;validate handle
        jnz     rsb_exit_error_vect_1

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

        mov     ebx,Options
        and     ebx,3                    ;options must be 1-3
        dec     ebx                      ;convert to 0-2
        shl     ebx,2
        jmp     rsb_table[ebx]
ALIGN 4

;/*
;** 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     eax,eax                   ;return 0 for error
        jmp     rsb_exit
ALIGN 4

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

rsb_restore_1:

        mov     esi,hddc

        ddc?    esi,<SURFACE>

        ASSUME  esi:PTR DDC, edi:PTR SURFACE

        mov     edi,[esi].ddc_npsd
        test    [edi].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     eax,sb_x_extent
        mov     loc_x_extent,eax
        mov     eax,sb_y_extent
        mov     loc_y_extent,eax
        mov     eax,sb_x_clip_lft
        mov     loc_x_clip_lft,eax
        mov     eax,sb_y_clip_btm
        mov     loc_y_clip_btm,eax
        mov     eax,sb_x_clip_rgt
        mov     loc_x_clip_rgt,eax
        mov     eax,sb_y_clip_top
        mov     loc_y_clip_top,eax
        mov     eax,yOffScreenScan
        mov     loc_y_offscreen,eax

        mov     esi,[esi].ddc_prddc       ;DS:SI = RDDC

        rddc?   esi

        ASSUME  esi:PTR RDDC

        mov     eax,[esi].rddc_ptsOrg.ptl_x
        mov     loc_x_origin,eax
        mov     eax,[esi].rddc_ptsOrg.ptl_y
        mov     loc_y_origin,eax
;/*
;** 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.
;*/

        mov     esi,lpRect               ;DS:SI --> rectangle boundaries

        ASSUME  esi:PTR RECTL

        mov     eax,[esi].rcl_xRight          ;get x2
        sub     eax,[esi].rcl_xLeft       ;compute x extent of given rect

ifdef FIREWALLS

        jg      rsb_x_ok

        rip     text,<Rectangle corners misordered>

rsb_x_ok:

endif
        sub     eax,loc_x_extent         ;subtract extent of saved rect
        sub     eax,loc_x_clip_lft       ;subtract amount by which saved
        sub     eax,loc_x_clip_rgt       ;  rectangle was clipped in x
        jnz     rsb_exit_error_vect_2   ;if not equal, get out

        mov     eax,[esi].rcl_yTop        ;get y2
        sub     eax,[esi].rcl_yBottom     ;compute y extent of given rect

ifdef FIREWALLS

        jg      rsb_y_ok
        rip     text,<Rectangle corners misordered>

rsb_y_ok:

endif
        sub     eax,loc_y_extent         ;subtract extent of saved rect
        sub     eax,loc_y_clip_btm       ;subtract amount by which saved
        sub     eax,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:

        xor     eax,eax                   ;return 0 for error
        jmp     rsb_exit
ALIGN 4

rsb_extents_ok:

        lea     edi,Points

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

        lodsd                           ;get x1
        add     eax,loc_x_origin         ;adjust by DC origin
        mov     ebx,eax                   ;save a copy
        cdq                 ;make AND mask based on sign(x1)
        and     eax,edx                   ;AX = min(x1,0)
        neg     eax                      ;AX >= 0
        mov     new_x_clip_lft,eax       ;save amt clipped off left
        add     ebx,eax                   ;x1 has been fully clipped
        cmp     eax,loc_x_clip_lft       ;at least as much clipped as before?
        jl      rsb_exit_error_vect_2   ;if no, error
        xchg    eax,ebx                   ;put x1 back into AX
        stosd                           ;store in Points

        .errnz  RECTL.rcl_xLeft

        lodsd                           ;get y1
        add     eax,loc_y_origin         ;adjust by DC origin
        mov     ebx,eax                   ;save a copy
        cdq
        and     eax,edx
        neg     eax                      ;AX >= 0
        mov     new_y_clip_btm,eax
        add     ebx,eax                   ;y1 has been fully clipped
        cmp     eax,loc_y_clip_btm
        jl      rsb_exit_error_vect_2
        xchg    eax,ebx                   ;put y1 back into AX
        stosd                           ;store in Points

        .errnz  RECTL.rcl_yBottom - RECTL.rcl_xLeft - 4

        lodsd                           ;get x2
        add     eax,loc_x_origin         ;adjust by DC origin
        mov     ebx,eax                   ;save a copy
        sub     eax,SCREEN_CX
        cdq
        not     edx
        and     eax,edx                   ;AX >= 0
        mov     new_x_clip_rgt,eax
        sub     ebx,eax                   ;x2 has been fully clipped
        cmp     eax,loc_x_clip_rgt
        jl      rsb_exit_error_vect_2
        xchg    eax,ebx                   ;put x2 back into AX
        stosd                           ;store in Points

        .errnz  RECTL.rcl_xRight - RECTL.rcl_yBottom - 4

        lodsd                           ;get y2
        add     eax,loc_y_origin         ;adjust by DC origin
        mov     ebx,eax                   ;save a copy
        sub     eax,SCREEN_CY
        cdq
        not     edx
        and     eax,edx                   ;AX >= 0
        mov     new_y_clip_top,eax
        sub     ebx,eax                   ;y2 has been fully clipped
        cmp     eax,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
ALIGN 4

clip_test_ok:

        xchg    eax,ebx                   ;put y2 back into AX
        stosd                           ;store in Points

        .errnz  RECTL.rcl_yTop - RECTL.rcl_xRight - 4
;/*
;** Do source computations:
;**
;** The first three general-case lines are optimized into the
;** following two:
;*/

        movsx   eax,loc_bit_align        ;loc_bit_align is in {0,...,7}
        mov     ebx,new_x_clip_lft       ;find out how much extra clipping
        sub     ebx,loc_x_clip_lft       ;known to be non-negative
        add     eax,ebx                   ;add in the clipping
        stosd

        .errnz  RECTL.rcl_xLeft

        mov     eax,SCREEN_CY
        sub     eax,loc_y_offscreen      ;highest y coord in source
        sub     eax,loc_y_extent         ;lowest y coord in source
        mov     ebx,new_y_clip_btm       ;get total lines clipped
        sub     ebx,loc_y_clip_btm       ;get extra lines clipped
        add     eax,ebx                   ;add in extra clipping
        stosd

        .errnz  RECTL.rcl_yBottom - RECTL.rcl_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     esi,hddc

        ASSUME  esi:PTR DDC

        or      [esi].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,[esi].ddc_ia.ia_ba.ba_fb
        mov     saved_iflags,al
        mov     al,[esi].ddc_pa.pa_ba.ba_fb
        mov     saved_pflags,al
        xor     eax,eax                   ;generic null parameter
        and     [esi].ddc_ia.ia_ba.ba_fb,al
        and     [esi].ddc_pa.pa_ba.ba_fb,al

        mov     esi,FunN                 ;part of MyFunN
ifdef DCAF                                                           ;          
;/*                                                                  ;          
;** Every primitive which affects the screen must be included in     ;          
;** the SCREEN bounds when asked for.                                ;          
;*/                                                                  ;          
        and     esi,COM_DRAW or COM_SCR_BOUND                        ;          
                                                                     ;          
else ; DCAF                                                          ;          
        and     esi,COM_DRAW             ;no correlation, bounds, xform, etc.
endif ; DCAF                                                         ;          
        mov     SI,NGreBitblt
        .errnz  RECTL.rcl_xLeft

        INVOKE  Bitblt, hdc, hdc, 3, ADDR Points, 0CCh, 0, 0, hddc, esi

ifdef FIREWALLS
;/*
;** We're supposed to ensure that Bitblt doesn't falter.  If it does,
;** it's our problem.
;*/

        or      eax,eax
        jnz     @F

        rip     text,<Bitblt error in RestoreScreenBits>
@@:

endif
        mov     esi,hddc              ;assuming DS = Data

        and     [esi].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     [esi].ddc_ia.ia_ba.ba_fb,al
        mov     al,saved_pflags
        mov     [esi].ddc_pa.pa_ba.ba_fb,al

rsb_free_bits::

        mov     eax,1                    ;prepare for "successful" exit

rsb_exit:

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

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

        push    eax                      ;save return code

        INVOKE  DosRequestVDD, hVideoVDD, 0, VVDSYSREQ_FREEMEM, 0, 0, nbOffScreenUsed,
                                ADDR pOffScreenUsed

        pop     eax                      ;restore return code

rsb_no_vdd:
        xor     edx,edx
        mov     pOffScreenUsed,edx   ;zero all these fields now
        mov     pOffScreenScan,edx
        mov     yOffScreenScan,edx
        mov     nbOffScreenUsed,edx
        and     fbOffScreen,NOT OFFSCR_TRASHED

rsb_really_exit:

;/*
;** return EAX
;*/

        call    leave_driver

rsb_exit_no_lock:

        fw_zero <ecx>

                        RET

RestoreScreenBits       ENDP


;/***************************************************************************
;*
;* 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
;*
;**************************************************************************/

        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

        public  ega_src_copy_bytes

ALIGN 4
ega_src_copy_bytes::


        DebugMsg <ega_src_copy_bytes, DISPATCH, ssb, CLIFFL>

        jecxz    escb_nobytes
        cld
        push    edx
        mov     ebx,SCREEN_CBSCAN
        sub     ebx,ecx                     ; BX = wrap (bytes to add to next
                                            ;   scan = 80 - CX)
        mov     edx,EGA_BASE + GRAF_ADDR
        mov     eax,0000h + GRAF_BIT_MASK   ; mask out all bits (use latches)
        out     dx,ax

        pop     eax
        mov     edx,ecx                   ; save inner_loop_count

escb_next_scanline:

        rep     movsb
        mov     ecx,edx                   ; restore inner_loop_count

        add     esi,ebx                   ; --> next source
        add     edi,ebx                   ; --> next destination
        dec     eax                       ; was that the last scan?
        jnz     escb_next_scanline

        mov     edx,EGA_BASE + GRAF_ADDR
        mov     eax,0FF00h + GRAF_BIT_MASK
        out     dx,ax

escb_nobytes:

        ret

        end
