;*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     55,132
        TITLE    Run Length Encoded Bitmap
        SUBTITLE Header

;/*****************************************************************************
;*
;* SOURCE FILE NAME = RLEBM.ASM
;*
;* DESCRIPTIVE NAME = Run Length Encoded Bitmap functions
;*
;*
;* VERSION      V2.0
;*
;* DATE         11/26/91
;*
;* DESCRIPTION
;*
;*    Draw RLE compressed bitmap at level of device driver.
;*
;*                RUN LENGTH ENCODED (RLE) BITMAPS
;*                --------------------------------
;*
;*    A RLE bitmap consists of two parts - an information header block and a
;*    coded buffer which we will refer to as the RLE bitmap.
;*
;*    Unlike normal bitmaps the RLE bitmap has the encoding of the information
;*    in a bitmap.  The incoding consists of a series of records of variable
;*    lengths, with the first byte of every record designating the type of the
;*    record.
;*
;*    The RLEs can be recorded in 8 bit mode or in 4 bit mode.  In 8 bit mode,
;*    all indices are byte valued, whereas in 4 bit mode the indices are
;*    specified in 4 bits and a byte will hold 2 of these indices.
;*
;*    The various types of records are classified below:
;*
;*  Encoding of a Runlength:
;*  ----------------------------
;*  FIRSTBYTE:  COUNT which is >= 1.
;*  SECONDBYTE: INDEX.
;*
;*    (8 BIT FORMAT)
;*      This is just a 2 BYTE record and encodes COUNT number of pels in the
;*      bitmap having the same color index. All indexes in the RLE records are
;*      with respect to the color table in the info header.
;*
;*    (4 BIT FORMAT)
;*      The second BYTE contains 2 color indices in 4 bits each and the first
;*      byte spcifies the number of pels of the alternating seq.  The color
;*      for the pels are obtained by alternately choosing the high and the low
;*      nibble, till COUNT number of pels are done.
;*
;*  Unencoded run:
;*  ------------------
;*  FIRSTBYTE: = 0.
;*  SECONDBYTE: COUNT >= 3. Followed by,
;*
;*    (8 BIT FORMAT)
;*      a string of BYTEs specifying the color indices, this string must be
;*      even, and be padded by a zero if the COUNT is ODD.
;*
;*    (4 BIT FORMAT)
;*      a string of bytes, each byte providing two 4 bit indices. The string
;*      must be even. There may be a filler byte and there maybe a byte with
;*      an index in just the left nibble.
;*
;*  Delta:
;*  ----------
;*  FIRSTBYTE:  = 0
;*  SECONDBYTE: = 2
;*  THIRDBYTE:  = DELTA_X (unsigned), >= 0
;*  FOURTHBYTE: = DELTA_Y (unsigned), >= 0, but not both 0
;*
;*    This is relative-jump record and implies that the decoding of the next
;*    RLE record is to start at (DELTA_X,DELTA_Y) wrt to the current position
;*    in the bitmap.
;*
;*  EOL (end-of-line):
;*  FIRSTBYTE:  = 0
;*  SECONDBYTE: = 0
;*
;*    Signifies the end of the current line in the bitmap. The decoding of the
;*    next record is to be done at the start of the next line.
;*
;*  EORLE (end-of-RLE):
;*  FIRSTBYTE:  = 0
;*  SECONDBYTE: = 1
;*
;*    Signifies the end of the encoding.
;*
;*
;* FUNCTIONS    Name
;*              ReadRLE
;*              SetGetByteMask
;*              DevicePartialSetPels
;*              DeviceFullSetPels
;*              DecideStrategy4Bit
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   11/26/91                     Written by Kenton E. Zahrt
;*
;*****************************************************************************/

        .386P
        .MODEL FLAT,SYSCALL

;/*
;** Included files
;*/

INCL_DDIBITMAPFILE EQU 1
INCL_GPIBITMAPS    EQU 1
INCL_GPIPRIMITIVES EQU 1
INCL_NOBASEAPI     EQU 1
INCL_SAADEFS       EQU 1
OS2_NOPMAPI        EQU 1
        INCLUDE PMGRE.INC

DINCL_BITMAP       EQU 1
DINCL_ENABLE       EQU 1
        INCLUDE DRIVER.INC
        INCLUDE EXTERN.INC
        INCLUDE PROTOS.INC
        INCLUDE EGAFAM.INC
        INCLUDE ASSERT.MAC

;/*
;** Extenal references
;*/

clean_up_before_exit_no_test PROTO SYSCALL      ; EXIT.BLT

;/*
;** Equates
;*/

RLE_CLIPPED EQU 00000001b       ; Clipping is required

;/*
;** Macros
;*/

;/*
;** The following macro provides a common frame definition for all functions
;** that use it.  The primary purpose is to allow a subroutine to be called and
;** use the parent's call frame (including local variables).  This can be
;** accomplished by disabling prologues and epilogues for the child function and
;** using the StdProc macro to define the PROC.  The child function now
;** understands the identifiers in the frame as offsets from EBP, and since EBP
;** is not updated by the prologue, it is a valid reference to the ancestor's
;** frame.
;*/

StdProc MACRO   Name,Visibility

        ALIGN   4

Name PROC SYSCALL Visibility uses EBX EDI ESI,
 pDestDDC    :DWORD, lDestX      :DWORD,
 lDestY      :DWORD, pRLEInfo    :DWORD,
 lLeftClipX  :DWORD, lBottomClipY:DWORD,
 lExtentX    :DWORD, lExtentY    :DWORD,
 pRLEBits    :DWORD, pColorTable :DWORD
 LOCAL  cDestScanCt      :DWORD         ; Total bytes in a scan
 LOCAL  cEncodeShft      :DWORD
 LOCAL  lX               :DWORD         ; Current lX on display
                                        ; surface
 LOCAL  lY               :DWORD         ; Current lY on display
                                                ; surface
 LOCAL  pfnDecideStrat   :DWORD         ; Decides strategy to adopt
 LOCAL  pfnGetAbsIndx    :DWORD         ; Addr of func to get next
                                                ; abs mode index
 LOCAL  pfnSetFullPels   :DWORD         ; Addr of func to set 8 pels
                                                ; in a byte
 LOCAL  pfnSetPartialPels:DWORD         ; Addr of func to set < 8
                                                ; pels in a byte
 LOCAL  pfnUpdateSrcPtr  :DWORD         ; Addr of func to update src
                                                ; pointer
 LOCAL  selScreen        :DWORD         ; Pointer to start of surface
 LOCAL  usNibbleToggle   :DWORD         ; Decides which nibble to use
 LOCAL  lRightClipX      :DWORD         ; Right of clip rectangle
 LOCAL  lTopClipY        :DWORD         ; Bottom of the clip rect
 LOCAL  fbSurfaceFlags   :BYTE          ; Defines the following flags
 LOCAL  Bspecial4bppMask :BYTE          ; Mask for encoded 4-bit RLE
                                                ; runs
ENDM    ; StdProc

;/*
;** Private functions
;*/

        .CODE

        SUBTITLE ReadRLE
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = ReadRLE
;*
;* DESCRIPTION   = Reads a BYTE from a encoded RLE buffer.
;*
;*                 Registers modified:
;*                   EAX ESI
;*
;* INPUT         = ESI --> RLE buffer
;*
;* OUTPUT        = AL  = Byte from buffer
;*                 ESI = Incremented
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN   4

ReadRLE PROC SYSCALL PRIVATE

        LODSB
        RET

ReadRLE ENDP

        SUBTITLE SetGetSegment
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetGetSegment
;*
;* DESCRIPTION   =
;*
;*     Gets a portion of the current segment from (lX,lY) to (lX+AX,lY) which
;*     fits within the clip rectangle.
;*
;*     We will divide the region surrounding the clip rectangle into 4 areas as
;*     shown below.  The segment is totally clipped if it lies totally in any
;*     of the four regions.  If it is not totally clipped, there can be 4 ways
;*     that the segment may reside.  These are all shown below.
;*
;*                                       region-1
;*                 <---------------------------------------------------->
;*                       |                                      |
;*                       |                                      |
;*                       |                                      |
;*          region-4     |          The Clip Rectangle          |  region-2
;*                       |                                      |
;*                 type_1|              type_2            type_3|
;*                 ------|------      ----------          ------|-----
;*                 ------|--------------------------------------|----- type_4
;*                 <---------------------------------------------------->
;*                                       region-3
;*
;*
;*
;*
;*
;* INPUT         = (lX,lY)           - Start of segment for current RLE record
;*                 (ClipLft,ClipTop) - Top left of the clip rectangle
;*                 (ClipRgt,ClipBot) - Bottom right of the clip rectangle
;*                 EAX               - Number of pels in the segment
;*
;* OUTPUT        = ECX - No of pels within clip rectangle, 0 => total clip
;*                       (if CX=0, rest is not valid)
;*                 EAX - No of pels clipped on left
;*                 EBX - No of pels clipped on right
;*                 EDI - Absisca of first visible pel in the segment
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc SetGetSegment,PUBLIC

        DebugMsg <Entering RLEBM.SetGetSegment,Private,KEZ>

        TEST    fbSurfaceFlags,RLE_CLIPPED
        JZ      SetGetSegment_not_clipped
        MOV     EBX,lX                          ; Get left abscissa of segment
        MOV     EDI,lY                          ; Get ordinate
        XOR     ECX,ECX                         ; Assume total clip

;/*
;** If lY is > ClipTop (region-3) or < ClipBot (region_1) it is totall clipped.
;*/

        CMP     EDI,lBottomClipY
        JB      SetGetSegment_end               ; Segment is totaly clipped
        CMP     EDI,lTopClipY
        JAE     SetGetSegment_end               ; Segment is totaly clipped

;/*
;** lY ordinate is OK, but we may still be in region-2 or -4.
;*/

        CMP     EBX,lRightClipX         ; Are we in region-2
        JAE     SetGetSegment_end       ;  Y - So nothing is visible
        PUSH    EAX                     ; Save extent
        ADD     EAX,EBX                 ; Ax has coord just past rt edge of
                                        ; seg
        CMP     EAX,lLeftClipX          ; Are we in region-4
        POP     EAX                     ; Get back extent in EAX
        JBE     SetGetSegment_end       ; Another total clip case

;/*
;** At least a part of the segment is visible. Find out the type of clip.
;*/

        MOV     ECX,EAX                 ; Assume all is visible
        XOR     EAX,EAX                 ; and nothing cliped on left
        CMP     EBX,lLeftClipX          ; Do we start on the left edge?
        JE      Set_start_on_lft_edge   ;  Y
        JG      Set_clip_type_2_or_3    ;  Can't be clip type 1 or 4

;/*
;** We have clip type 1 or 4, so a portion is clipped off the left end.
;*/

        SUB     EBX,lLeftClipX
        NEG     EBX                     ; No. of pels clipped from left end
        SUB     ECX,EBX                 ; EBX less pixels to draw
        MOV     EAX,EBX                 ; Remember left cliped pixels

Set_start_on_lft_edge:
        MOV     EDI,lLeftClipX          ; Start on left edge
        MOV     EBX,lExtentX            ; Get extent not exceeding clip rect
        JMP     SetGetSegment_right_clip

Set_clip_type_2_or_3:
        MOV     EDI,EBX                 ; Start abscissa of visible segment
        SUB     EBX,lRightClipX         ; EBX has left X of segment
        NEG     EBX
;/*
;**   ECX is wanted extent
;**   EBX is max alowed extent
;*/

SetGetSegment_right_clip:
        CMP     ECX,EBX                         ; Are we clipped on the right?
        JA      SetGetSegment_right_clipped
        XOR     EBX,EBX                         ;  N - Set rt. clipped to zero
        JMP     SetGetSegment_end

SetGetSegment_right_clipped:
        SUB     ECX,EBX                 ; We are right cliped,set...
        XCHG    ECX,EBX
;/*
;**   EAX is amount left clipped
;**   ECX is clipped extent
;**   EBX is amount right clipped
;*/

SetGetSegment_end:
        JMP     SetGetSegment_Exit

SetGetSegment_not_clipped:
        MOV     EDI,lX
        MOV     ECX,EAX
        XOR     EAX,EAX
        XOR     EBX,EBX

SetGetSegment_Exit:

        DebugMsg <Leaving SetGetSegment>

        RET

SetGetSegment ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE SetStartOffset
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetStartOffset
;*
;* DESCRIPTION   =  Find the offset in surfaces that will not exceed 64k in size.
;*
;* INPUT         = (EDI,lY) - Start coordinate on display surface.
;* OUTPUT        = EDI --> Start byte on display surface.
;*                 DL   -  Number of bits in 1st byte to left of start bit.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc SetStartOffset,PUBLIC

        MOV     EAX,lDestY
        SUB     EAX,lY
        ADD     EAX,lBottomClipY        ; Current scan
        MUL     cDestScanCt             ; Number of bytes in a scan
        assert  EDX,E,0
        SUB     EDI,lLeftClipX
        ADD     EDI,lDestX              ; Get the X origin
        MOV     EDX,EDI                 ; Get start X
        SHR     EDI,3                   ; Get number of complete bytes
        ADD     EDI,EAX                 ; EDI has correct offset
        ADD     EDI,selScreen ;DAK
        AND     DL,00000111b            ; Get number of bits before start bit

        RET

SetStartOffset ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE SetGetBitMask
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetGetBitMask
;*
;* DESCRIPTION   = Computes pointer to start byte on destination surface and
;*                 bit mask for the first bit in the mask.
;*
;*                 Registers destroyed:
;*                                 AL,BX,DX
;*
;*
;* INPUT         = (EDI,lY) - Start point on surface.
;*                 ECX      - No of pels in the segment.
;* OUTPUT        = EDI - Start byte on the surface.
;*                 AH  - Bitmask.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc SetGetBitMask,PUBLIC

        CALL    SetStartOffset

;/*
;** Returns start offset in EDI, StartX & 00000111b in DL.
;*/

        PUSH    ECX     ; Save count
        MOV     CL,DL   ; Get no of bits to left of start
        MOV     AH,80h  ; Set mask to left end
        SHR     AH,CL   ; Set mask to the correct bit
        POP     ECX     ; Restore

        RET

SetGetBitMask ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE SetMultipelSegment
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetMultipelSegment
;*
;* DESCRIPTION   = Draws a segment onto the display surface, where the pels
;*                 in the segment are of different colors.
;*
;* INPUT         = (EDI,lY) - Start coordinate of the segment.
;*                 ECX      - Number of pels in the segment.
;*                 ESI    --> Index for the first pel in the segment.
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc SetMultipelSegment,PUBLIC

        DebugMsg <Entering RLEBM.SetMultipelSegment,Private,KEZ>
;/*
;** Calculate the start offset in the surface and the the bit mask and start
;** byte mask.
;*/

        CALL    SetGetBitMask
;/*
;** EDI points to byte on destination surface to hold the first bit.  AH has
;** the bit mask.
;*/

        MOV     EBX,pColorTable         ; Address of the translation table
        MOV     DX,EGA_BASE+GRAF_DATA   ; Address of bitmask register

Set_multipel_loop:

        CALL    pfnGetAbsIndx           ; Get next pel index
        XLAT    [EBX]                   ; Get the color value for the index

;/*
;** AL has color and AH has mask byte.
;*/

        CALL    pfnSetPartialPels       ; Set the pels
        ROR     AH,1                    ; Update mask to next pel
        ADC     EDI,0                   ; Update byte if wrap around
        LOOP    Set_multipel_loop

        DebugMsg <Leaving SetMultipelSegment>

        RET

SetMultipelSegment ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE SetGetByteMask
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetGetByteMask
;*
;* DESCRIPTION   = Computes pointer to start byte on destination surface and
;*                 byte masks for the first and last bytes and the number of
;*                 intermediate bytes.
;*
;*                 Registers destroyed:
;*                                 AX,DX
;*
;* INPUT         = (EDI,lY) - Start point on surface.
;*                 ECX      - No of pels in the segment.
;*
;* OUTPUT        = EDI --> Start byte on the surface.
;*                 BL   -  First byte mask.
;*                 BH   -  Last byte mask.
;*                 ECX  -  Number of intermediate bytes (may be 0)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

SetGetByteMask PROC SYSCALL PUBLIC

        CALL    SetStartOffset

;/*
;** Returns with EDI pointing to start offset and StrtX & 00000111b in DL.
;*/

        MOV     EAX,ECX         ; Save extent of segment

;/*
;** Get the start bitmask in BL
;*/

        MOV     CL,DL           ; Get number of bits before start bit
        MOV     BL,80h          ; Initialize bitmask at left end
        SHR     BL,CL           ; Set correct start mask
        MOV     ECX,EAX         ; Get back pel count
        DEC     ECX             ; Exclude last pel
        MOV     EAX,ECX
        SHR     EAX,3           ; Get number of complete bytes in run
        AND     ECX,00000111b   ; and the number of bits left
        MOV     BH,BL           ; Save start mask
        ROR     BL,CL           ; Get end mask
        MOV     ECX,EAX         ; Number of bytes
        CMP     BH,BL           ; Test for wrap around
        ADC     ECX,0           ; One more byte if so
        NEG     BL              ; Ending byte mask
        ADD     BH,BH
        DEC     BH              ; Starting byte mask
        XCHG    BH,BL           ; BL has start mask and BH has end mask

        RET

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

SetGetByteMask ENDP

        SUBTITLE SetOnepelSegment
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = SetOnepelSegment
;*
;* DESCRIPTION   = Draws a segment onto the display surface, where the pels
;*                 in the segment are all of the same color.
;*
;* INPUT         = (EDI,lY) - Start coordinate of the segment.
;*                 ECX      - No pels in the segment
;*                 ESI      - Pointer to the index for the pel color
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc SetOnepelSegment,PUBLIC

        DebugMsg <Entering RLEBM.SetOnepelSegment,Private,KEZ>
;/*
;** Calculate the start offset in the surface and the the bit mask and start
;** byte mask.
;*/

        INVOKE  SetGetByteMask
;/*
;** EDI points to byte on destination surface to hold the first bit.  BL has the
;** first byte mask, BH has the last byte mask and ECX has the inner byte count.
;*/

        MOV     EDX,EBX                 ; Save masks
        MOV     EBX,pColorTable         ; Address of the translation table
        XCHG    ECX,cEncodeShft
        MOV     AL,[ESI]                ; Fetch the encode byte
        SHR     AL,CL                   ; If 4bpp,left nibble of interest
        XCHG    ECX,cEncodeShft
        XLAT    [EBX]                   ; Get the color value for the index
        MOV     EBX,EDX                 ; Get back masks
        MOV     DX,EGA_BASE+GRAF_DATA   ; Address of bitmask register

Set_onepel_first:

;/*
;** If ECX is 0, the two byte masks are to be combined and only one byte to be
;** done.
;*/

        MOV     AH,BL                   ; Get first byte mask
        JECXZ   Set_onepel_last         ; Do the last BYTE only.
;/*
;** Do the first byte
;** EDI --> destination byte. AH has byte mask. AL has pel color.
;*/

        CALL    pfnSetPartialPels       ; Set the pels for partial byte
        INC     EDI                     ; Point to next byte
        DEC     ECX                     ; First pel was incuded in count

;/*
;** Now do the intermediate bytes and 8 bits will be set in a call.
;*/

        MOV     AH,0FFh                 ; All bits enabled
        JECXZ   Set_onepel_last         ; No intermediate bytes
        XCHG    AH,AL                   ; Get mask value
        OUT     DX,AL                   ; Set up mask
        XCHG    AH,AL                   ; Restore original values

Set_onepel_loop:
        CALL    pfnSetFullPels          ; Do a complete byte
        INC     EDI                     ; Do next byte
        LOOP    Set_onepel_loop         ; Complete the intermediate runs

;/*
;** Now do the last BYTE in the run.
;*/

Set_onepel_last:
        AND     AH,BH                   ; Combine with previous mask
        CALL    pfnSetPartialPels       ; Set pels in the last byte

        DebugMsg <Leaving SetOnepelSegment>

        RET

SetOnepelSegment ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE Set4BPPOnepelSegment
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = Set4BPPOnepelSegment
;*
;* DESCRIPTION   = Draws a segment onto the display surface, where the pels in
;*                 the segment are of alternating colors.  This is used for
;*                 playing back encoded pels in 4 bit RLE.  We basically will
;*                 make two passes, the first pass will set all the pels of
;*                 the first color and the next will set all the pels of the
;*                 other color.
;*
;* INPUT         = (EDI,lY) - Start coordinate of the segment.
;*                 ECX      - No pels in the segment.
;*                 ESI      - Pointer to the index for the 2 pel colors.
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc Set4BPPOnepelSegment,PUBLIC

        DebugMsg <Entering RLEBM.Set4BPPOnepelSegment,Private,KEZ>

        pushem  EDI,ECX,ESI             ; Save these  values
        CALL    SetOnepelSegment        ; Do the entire thing with one color
        popem   EDI,ECX,ESI             ; Get back saved values
        MOV     EAX,pfnSetFullPels      ; Get the full pel address
        PUSH    EAX                     ; Save it
        MOV     EAX,pfnSetPartialPels   ; Get the partial pel address
        MOV     pfnSetFullPels,EAX      ; Will need masking all through
;/*
;** Calculate the start offset in the surface and the the bit mask and start
;** byte mask.
;*/

        INVOKE  SetGetByteMask
;/*
;** Get the position of the first bit in the byte and decide whether we use
;** 10101010 or 01010101 as the running mask.
;*/

        pushem  EBX,ECX                 ; Save
        MOV     ECX,lX                  ; Get starting coordinate
        AND     CL,7                    ; Get the starting bit number
        MOV     BL,80h                  ; Initial position
        SHR     BL,CL                   ; Get the starting bit position
        MOV     CL,10101010b            ; First type of mask
        MOV     CH,10101010b            ; Mask for second pel if CL not good
        AND     CL,BL                   ; Did it match ?
        JZ      @f                      ; CH has runing mask for 2nd pel
        MOV     CH,01010101b            ; Mask for second bit

@@:
        MOV     AL,CH                   ; Get the mask
        MOV     AH,CH                   ; Mask in AL & AH
        popem   EBX,ECX                 ; Get back actual masks
        AND     EBX,EAX                 ; Update masks
        MOV     Bspecial4bppMask,AL     ; Save it
;/*
;** EDI points to byte on destination surface to hold the first bit.  BL has the
;** first byte mask, BH has the last byte mask and ECX has the inner byte count.
;*/

        MOV     EDX,EBX                 ; Save masks
        MOV     EBX,pColorTable         ; Address of the translation table
        MOV     AL,[ESI]                ; Get the index
        AND     AL,0Fh                  ; Interested in second nibble
        XLAT    [EBX]                   ; Get the color value for the index
        MOV     EBX,EDX                 ; Get back masks
        MOV     DX,EGA_BASE+GRAF_DATA   ; Address of bitmask register

Set_4bpp_onepel_first:
;/*
;** If ECX is 0, the two byte masks are to be combined and only one byte to be
;** done.
;*/

        MOV     AH,BL                   ; Get first byte mask
        JECXZ   Set_4bpp_onepel_last    ; Do the last BYTE only.
;/*
;** Do the first byte.
;** EDI --> Destination byte.  AH has byte mask.  AL has pel color.
;*/

        CALL    pfnSetPartialPels       ; Set the pels for partial byte
        INC     EDI                     ; Point to next byte
        DEC     ECX                     ; First pel was incuded in count

;/*
;** Now do the intermediate bytes and 8 bits will be set in a call.
;*/

        MOV     AH,Bspecial4bppMask     ; All bits enabled
        JECXZ   Set_4bpp_onepel_last    ; No intermediate bytes
        XCHG    AH,AL                   ; Get mask value
        OUT     DX,AL                   ; Set up mask
        XCHG    AH,AL                   ; Restore original values

Set_4bpp_onepel_loop:
        CALL    pfnSetFullPels          ; Do a complete byte
        INC     EDI                     ; Do next byte
        LOOP    Set_4bpp_onepel_loop    ; Complete the intermediate runs

;/*
;** Now do the last BYTE in the run.
;*/

Set_4bpp_onepel_last:
        AND     AH,BH                   ; Combine with previous mask
        CALL    pfnSetPartialPels       ; Set pels in the last byte
        POP     EAX                     ; Get back the full pel address
        MOV     pfnSetFullPels,EAX      ; Restore it

        DebugMsg <Leaving Set4BPPOnepelSegment>

        RET

Set4BPPOnepelSegment ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

;/*
;** Public functions
;*/

        SUBTITLE RLEDrawBits
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = RLEDrawBits
;*
;* DESCRIPTION   = Display Run-Length-Encoded bitmaps.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

StdProc RLEDrawBits,PUBLIC

        DebugMsg <Entering RLEBM.RLEDrawBits,Public,KEZ>

        CLD

;/*
;** Set up the address to the first byte of the screen, and adjust lY origi
;*/

RDB_Dst_info:
        MOV     ESI,pDestDDC            ; ESI => the dest. DDC
        MOV     ESI,[ESI].DDC.ddc_npsd  ; ESI => the dest. surface
        MOV     EAX,[ESI].SURFACE.sd_pBits      ; The screen address
        MOV     selScreen,EAX
        MOV     cDestScanCt,SCREEN_DSCAN

;/*
;** Calculate the source rectangle.
;*/

RDB_Src_rect:
        XOR     EAX,EAX                 ; The RLE starts from origin(0,0)
        MOV     lX,EAX                  ; Start lX coordinate
        MOV     lY,EAX                  ; Start lY coordinate
        MOV     EAX,lLeftClipX          ; The left edge
        ADD     EAX,lExtentX
        MOV     lRightClipX,EAX         ; The right edge
        MOV     EAX,lBottomClipY                ; The top edge
        ADD     EAX,lExtentY
        MOV     lTopClipY,EAX           ; The Bottom edge

;/*
;** Special care must be taken if the width of the rectangle to draw does not
;** cover the full scanwidth. We treat that case as a clipped rectangle.
;*/

RDB_Clip_test:
        MOV     ESI,pRLEInfo            ; ESI points to RLE info block
        MOV     EAX,lExtentX
        CMP     EAX,[ESI].BITMAPINFOHEADER2.bmp2_cx     ; If the drawing width
                                                        ; is not equal to the
        SETNE   fbSurfaceFlags                          ; bitmap width,
                                                        ; indicate clipped
                                                        ; rect
        .ERRNZ  RLE_CLIPPED-00000001b
        MOV     EAX,lExtentY
        CMP     EAX,[ESI].BITMAPINFOHEADER2.bmp2_cy     ; If the drawing
        SETNE   AL                                      ; height is not equal
        OR      fbSurfaceFlags,AL                       ; to the bitmap
                                                        ; height, indicate
                                                        ; clipped rect

;/*
;** Depending on 4 bit or 8 bit encoding set up function address variables
;*/

RDB_4bpp_addr:
        CMP     [ESI].BITMAPINFOHEADER2.bmp2_ulCompression,BCA_RLE8
        JE      RDB_8bpp_addr                           ; Yes

;/*
;** Set up function addresses for 4 bit encoding.
;*/

        MOV     EAX,4                   ; We need only the encode nibble
        MOV     EBX,DecideStrategy4Bit
        MOV     ECX,UpdateSourcePointer4Bit
        MOV     EDX,GetNextAbsoluteIndex4Bit
        JMP     RDB_Have_addr

;/*
;** Set up function addresses for 8 bit encoding.
;*/

RDB_8bpp_addr:
        XOR     EAX,EAX                 ; We need the full encode byte
        MOV     EBX,SameColorStrategy
        MOV     ECX,Update_src_ptr_8_bit
        MOV     EDX,ReadRLE

RDB_Have_addr:
        MOV     cEncodeShft,EAX
        MOV     pfnDecideStrat,EBX
        MOV     pfnUpdateSrcPtr,ECX
        MOV     pfnGetAbsIndx,EDX

;/*
;** Set up the routine addresses for the case where the display surface is a
;** device.
;*/

        MOV     EAX,DevicePartialSetPels
        MOV     pfnSetPartialPels,EAX           ; Set bits in partial bytes
        MOV     EAX,DeviceFullSetPels
        MOV     pfnSetFullPels,EAX              ; Set bits in complete bytes

;/*
;** Cursor Exclusion for a device destination.
;*/

RDB_Cursor_exclude:
        MOV     ESI,lExtentX
        MOV     EDI,lExtentY
        DEC     ESI                     ; Make extents inclusive of last point
        DEC     EDI
        MOV     ECX,lDestX              ; Set left
        MOV     EDX,lDestY              ; Set top
        ADD     ESI,ECX                 ; Set right
        ADD     EDI,EDX                 ; Set bottom
        INVOKE  far_exclude             ; Exclude the area from the screen

;/*
;** The lY orientation of an RLE is inverse of the         convention.This thus
;** means that we start setting bits to/geting bits from the display surface
;** at (lDestX,lDestY+lExtentY-1). Set up the start lX and lY coordinates.
;*/

RDB_Adjust_lDestY:
        MOV     EAX,lDestY              ; Get the lY origin (top scan)
        ADD     EAX,lExtentY            ; Move it to the bottom of scan
        DEC     EAX
        MOV     lDestY,EAX              ; Adjusted lY origin

;/*
;** Set up the EGA registers for write mode 2.
;*/

RDB_Init_board:
        MOV     DX,EGA_BASE + GRAF_ADDR         ; Show color read mode to the
                                                ; EGA restoration code and
                                                ; the GRX controller address

;/*
;** Set up for READ 0/WRITE 2.
;*/

        MOV     AX,(M_COLOR_WRITE shl 8)+GRAF_MODE
        MOV     shadowed_graf_mode.vvr_value,AH         ; Must shadow this for
                                                        ; state code
        OUT     DX,AX
        JMP     @F                                      ; I/O delay

@@:

;/*
;** Set up the GRX address register with the index for the bit mask regist
;*/

        MOV     AL,GRAF_BIT_MASK
        OUT     DX,AL
        JMP     @F                      ; I/O delay

@@:
        MOV     ESI,pRLEBits            ; ESI points to rle buffer

;/*
;** The current position in the source is at (lX,lY).  The clipping rectangle is
;** from (lLeftClipX,lBottomClipY) to (lRightClipX,lTopClipY).
;*/

RDB_Set_decode_RLE:
        XOR     EAX,EAX                 ; Count is 1 byte
        MOV     usNibbleToggle,EAX      ; Used for 4 bit RLEs
        INVOKE  ReadRLE                 ; Get the next byte
        OR      AL,AL                   ; Is it 0
        JNZ     RDB_Set_encoded_run     ; The next byte has the color index.

;/*
;** Possibilities are: we may have,
;**       . End of Line            -  If next BYTE is 0,
;**       . End of RLE             -  If next BYTE is 1,
;**       . Delta                  -  If next BYTE is 2
;**       . Unencoded run          -  If next BYTE is 3 or more
;*/

        INVOKE  ReadRLE                 ; Get the next code byte
        CMP     AL,3                    ; Absolute mode ?
        JAE     RDB_Set_absolute_mode   ; Absolute mode of encoding
        CMP     AL,2                    ; Delta or segment cross
        JE      RDB_Set_delta           ; Yes
        OR      AL,AL                   ; End of line?
        JZ      RDB_Set_EOL             ; Yes
        JMP     RDB_Set_EORLE           ; End of encoding

;/*
;** We have an unencoded run, COUNT INDEX1 INDEX2 ...,with no. of indexes in
;** the run being in EAX.
;*/

RDB_Set_absolute_mode:
        PUSH    EAX                             ; Save the count of RLE
        TEST    fbSurfaceFlags,RLE_CLIPPED
        JZ      RDB_Set_multipel_not_cliped

;/*
;** Calculate the portion of the above segment that is within the clipping
;** rect.
;*/

        CALL    SetGetSegment                   ; Set up params for multipel

;/*                                                ; segment
;**   EAX is pixels cliped on left
;**   ECX is pixels visible
;**   EBX is pixels cliped on right
;*/

        CALL    pfnUpdateSrcPtr                 ; Advance over left clipped
                                                ; pels
        JECXZ   RDB_Set_multipel_update_X_Y     ; Segment is clipped

;/*
;** At least a part of the segment is visible. (DI,lY) has the start
;** coordinate, ECX has the extent and ESI points to the index of the first
;** pel in the visible part.
;*/

        PUSH    EBX                             ; Save right clipped count
        CALL    SetMultipelSegment              ; Draws the segment, pels of
                                                ; diff color
        POP     EAX                             ; Restore right clip count and
        CALL    pfnUpdateSrcPtr                 ; advance over clipped pels
        JMP     RDB_Set_multipel_update_X_Y

;/*
;** All the pels for this record will be drawn to the screen.
;*/

RDB_Set_multipel_not_cliped:
        MOV     EDI,lX
        MOV     ECX,EAX
        CALL    SetMultipelSegment      ; Draws the segment, pels of diff
                                        ; color

;/*
;** Now update (lX,lY) to account for the coding in the present RLE record.
;*/

RDB_Set_multipel_update_X_Y:
        MOV     EAX,usNibbleToggle
        CALL    pfnUpdateSrcPtr
        POP     EAX                     ; Get back count of indices in record
        ADD     lX,EAX                  ; Update the abscissa.
        TEST    ESI,1                   ; WORD align source pointer
        JZ      RDB_Set_decode_RLE
        INVOKE  ReadRLE

@@:
        JMP     RDB_Set_decode_RLE      ; Continue with next record

;/*
;** The record is a COUNT INDEX type of record and all pels in the segment are
;** of the same color. If we are in 4 bit encoding mode, two things could
;** happen:
;**   . If the two nibbles are the same, we will treat this to be a truly
;**     encoded record just as we do in the case of 8 bit mode.
;**
;**   . If the two nibbels do not have the same value then we will do the
;**     decoding in two passes, the first one will set all the pels in the run
;**     to the first color and the next one will alternating pels to the
;**     second color.
;**
;** We call a function to decide on the display strategy. First, obtain the
;** start coordinate in the segment and extent wrt the clip rectangle.
;*/

RDB_Set_encoded_run:
        PUSH    EAX                             ; Save count
        CALL    SetGetSegment                   ; Get start (x,y) in segment
                                                ; and count
        JECXZ   RDB_Set_onepel_update_X_Y       ; The whole segment is clipped
        CALL    pfnDecideStrat                  ; Decide how to treat 4 bit
                                                ; encoding
        JNC     RDB_Treat_as_encoded            ; Stretch of same color

;/*
;** We have a stretch of alternating colors, so we will call a routine to do
;** this strtech in two passes.
;*/

        CALL    Set4BPPOnepelSegment            ; Draw visible part of segment
        JMP     RDB_Set_onepel_update_X_Y

;/*
;** At least a part is visible.  (EAX,lY) has start coordinate, ECX has extent
;** ESI points to the pel index to be used for each pel in the segment.
;*/

RDB_Treat_as_encoded:
        CALL    SetOnepelSegment        ; Draw the visible part of the segment

;/*
;** Update (lX,lY) for start of next RLE record.
;*/

RDB_Set_onepel_update_X_Y:
        INVOKE  ReadRLE                         ; Advance source pointer
        POP     EAX                             ; Get back count of pels in
                                                ; RLE record
        ADD     lX,EAX                          ; Move lX, lY stays the same,
                                                ; ESI is OK
        JMP     RDB_Set_decode_RLE              ; Decode next RLE record.

;/*
;** We have reached a delta record.
;*/

RDB_Set_delta:
        INVOKE  ReadRLE                         ; Get next byte
        XOR     AH,AH                           ; Zeroize
        MOV     EBX,EAX                         ; Save in EBX
        INVOKE  ReadRLE                         ; Fetch the next byte

;/*
;** We have a DELTA encoded record, DELTA_X is in EBX and DELTA_Y in EAX.
;** Update the current position to be (X+DELTA_X,Y+DELTA_Y).
;*/

        ADD     lX,EBX                  ; Update lX
        ADD     EAX,lY                  ; Update lY
        CMP     EAX,lTopClipY           ; Do we pass the top edge?
        JAE     RDB_Set_EORLE           ; Yes, we are done. go to reset HW
        MOV     lY,EAX                  ; Yes, no, actually update lY
        JMP     RDB_Set_decode_RLE      ; Decode next  record

;/*
;** we have an end-of-line record. The action is to update lY and have the lX
;** same as the left edge of the destination rectangle. Since we are traversing
;** the surface in the reverse direction, we will have to decrement Y.
;*/

RDB_Set_EOL:
        MOV     lX,EAX                  ; Reset the abscissa
        INC     lY                      ; Point to next scan
        MOV     EAX,lTopClipY
        CMP     lY,EAX                  ; Do we pass the top edge?
        JB      RDB_Set_decode_RLE      ;  N - Next RLE

;/*
;** We are done with the blitting. Restore device programming if necessary
;*/

RDB_Set_EORLE:
        INVOKE  clean_up_before_exit_no_test    ; Reinitialize the device
        INVOKE  far_unexclude                   ; Allow cursor to be drawn

rdb_exit:

        DebugMsg <Leaving RLEDrawBits>

        RET

RLEDrawBits ENDP

        SUBTITLE DevicePartialSetPels
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = DevicePartialSetPels
;*
;* DESCRIPTION   = Routine to output a partial byte to a screen device.
;*
;* INPUT         = EDI --> Destination byte where run of pels start.
;*                 AL   -  Color values of the pels
;*                 AH   -  Bitmask for the byte
;*                 EDX --> Address of bitmask register for screen, width of a
;*                         scan for memory maps.
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

DevicePartialSetPels PROC SYSCALL PUBLIC

        PUSH    EAX
        XCHG    AH,AL           ; Get mask byte into AL
        OUT     DX,AL           ; The mask is set
        XCHG    [EDI],AH        ; Set the pel colors
        POP     EAX

        RET

DevicePartialSetPels ENDP

        SUBTITLE DeviceFullSetPels
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = DeviceFullSetPels
;*
;* DESCRIPTION   = Routine to output a partial byte to a screen device.
;*
;* INPUT         = EDI --> Destination byte where run of pels start.
;*                 AL   -  Color values of the pels.
;*                 AH   -  Bitmask for the byte.
;*                 EDX  -  Address of bitmask register for screen, width of a
;*                         scan for memory maps.
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

DeviceFullSetPels PROC SYSCALL PUBLIC

        PUSH    EAX
        XCHG    [EDI],AL        ; Set the pel colors
        POP     EAX

        RET

DeviceFullSetPels ENDP

        SUBTITLE UpdateSourcePointer4Bit
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = UpdateSourcePointer4Bit
;*
;* DESCRIPTION   = The following two routines are for updating the source
;*                 pointer after doing an absolute run of pels.  The first
;*                 routine is for 8 bit encoding and the next is for 4 bit
;*                 encoding.
;*
;* INPUT         = ESI --> Start of last record.
;*                 EAX  =  Number of pels encoded in last record.
;*
;* OUTPUT        = ESI --> Start of next record.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc UpdateSourcePointer4Bit,PUBLIC

        ADD     EAX,usNibbleToggle      ; Handle odd nibble dudes
        MOV     usNibbleToggle,EAX      ; Save low bit in usNibbleToggle
        AND     usNibbleToggle,1
        SHR     EAX,1                   ; Convert nibble count to bytes

Update_src_ptr_8_bit::
        ADD     ESI,EAX                 ; Point to next RLE record

        RET

UpdateSourcePointer4Bit ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE GetNextAbsoluteIndex4Bit
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = GetNextAbsoluteIndex4Bit
;*
;* DESCRIPTION   = Routine for 4 bit encoding.  BitToggle if 0, return the
;*                 left nibble of the current byte, else return the right
;*                 nibble and bump the pointer.
;*
;*
;*
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        OPTION  PROLOGUE:NONE
        OPTION  EPILOGUE:NONE

StdProc GetNextAbsoluteIndex4Bit,PUBLIC

        XOR     usNibbleToggle,1        ; Update the toggle switch
        TEST    usNibbleToggle,1        ; Test state
        JNZ     Left_nibble_4_bit       ; Was zero, so return left nibble

;/*
;** The state was 1, return the right nibble and bump the pointer.
;*/

        INVOKE  ReadRLE
        AND     AL,00001111b            ; Retain the left nibble.
        JMP     GNAI_Exit

;/*
;** The state was 0, retun the left nibble rotated into the right nibble.
;*/

Left_nibble_4_bit:
        MOV     AL,[ESI]                ; Get the current byte
        SHR     AL,4                    ; Get the left nibble into the right

GNAI_Exit:
        RET

GetNextAbsoluteIndex4Bit ENDP

        OPTION  PROLOGUE:PrologueDef
        OPTION  EPILOGUE:EpilogueDef

        SUBTITLE DecideStrategy4Bit
        PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = DecideStrategy4Bit
;*
;* DESCRIPTION   = The following routine decides what strategy is to be
;*                 adopted for handling encoded run in 4 bit mode.  For
;*                 stretches of the same color we will reset the carry flag
;*                 and return, but for alternate color stretches, we will
;*                 set carry and return.  For 8 bit mode, the address of the
;*                 part that clears the carry should be saved in the calling
;*                 memory variable.
;*
;* INPUT         = ESI --> Encode records index byte.
;*                 EAX --- To be preserved.
;* OUTPUT        = CARRY -- Set if the run is of alternate colors.
;*                 CARRY -- Cleared if the run is of the same color
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

DecideStrategy4Bit PROC SYSCALL PUBLIC

        PUSH    EAX                     ; Save EAX
        MOV     AL,[ESI]                ; Get the index
        MOV     AH,AL                   ; Have a copy
        AND     AL,00001111b            ; Get the lower index
        SHR     AH,4                    ; Have the shifted upper index
        CMP     AL,AH                   ; Compare the two
        POP     EAX                     ; Restore EAX
        JE      SameColorStrategy       ; stretch is of same color
        STC                             ; Set carry flag, different color
        JMP     DS4B_Exit

SameColorStrategy::
        CLC                             ; Reset carry

DS4B_Exit:
        RET

DecideStrategy4Bit ENDP

        END
