;*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 = RLEBM.ASM
;*
;* DESCRIPTIVE NAME = Run Length Encoded Bitmap functions
;*
;*
;* VERSION      V2.0
;*
;* DATE         05/24/90
;*
;* 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    RLEDrawBits
;*              ReadRLE
;*              set_get_segment
;*              set_multipel_segment
;*              set_onepel_segment
;*              set_4bpp_onepel_segment
;*              dev_set_pels_partial
;*              dev_set_pels_full
;*              set_get_bit_mask
;*              set_get_byte_mask
;*              set_start_offset
;*              update_src_ptr_4_bit
;*              get_next_abs_index_4_bit
;*              decide_strategy_4_bit
;*
;* NOTES        
;*              
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/27/89                     Amit Chatterjee [amitc]
;*                                Created for         for DIScreenBlt
;*   05/24/90                     Viroon Touranachun [viroont]
;*                                Adapted for PM
;*****************************************************************************/

        .286P

        .xlist
        include cmacros.inc
INCL_GPIBITMAPS         equ                      1       ; for bitmap info structure
        include pmgre.inc
        include driver.inc
        include display.inc
        include egafam.inc
        include egamemf.inc
        include assert.mac
        include iodelay.inc
        .list

        CPUMode 386

        externFP    far_exclude         ; CURSORSC.ASM
        externFP    far_unexclude       ; CURSORSC.ASM
        externA     SCREEN_DSCAN        ; EGAMEMD.ASM
        externA     DOSHUGEINCR

RLE_CLIPPED     equ     00000001b       ;clipping is required

sBegin  PtrData
        externB     shadowed_graf_mode
sEnd    PtrData

sBegin  Code
        assumes cs,Code
        assumes ds,nothing
        assumes es,nothing

        externNP    clean_up_before_exit_no_test; EXIT.BLT

        externW     MyPtrCodeData


cProc    RLEDrawBits,<NEAR,PUBLIC,WIN,PASCAL>,<si,di>

        parmD   pddcDst                           ;Destination ddc
        parmW   xDst                              ;Destination x origin
        parmW   yDst                              ;Destination y origin
        parmD   lpRLEInfo                         ;Source bitmap info structure
        parmW   xClipLft                          ;Source x origin
        parmW   yClipBot                          ;Source y origin
        parmW   cxExt                             ;x extent of the BLT
        parmW   cyExt                             ;y extent of the BLT
        parmD   lpRLEbits                         ;pointer to RLE encoded buffer
        parmD   lpClrTab                          ;pointer to mapping color table
        

        localW  selScreen                         ;pointer to start of surface
        localW  xClipRgt                          ;right of clip rectangle
        localW  yClipTop                          ;bottom of the clip rectangle
        localW  X                                 ;current X on display surface
        localW  Y                                 ;current Y on display surface
        localW  cEncodeShft

        localB  fbSurfaceFlags                    ;defines the following flags
        localW  cbDstScan                         ;total bytes in a scan
        localW  usNibbleToggle                    ;decides which nibble to use.
        localB  bspecial_4bpp_mask                ;mask for encoded 4bit rle runs

        localW  pfnSetPartialPels                 ;addr of fn to set < 8 pels in a byte
        localW  pfnSetFullPels                    ;addr of fn to set 8 pels in a byte
        localW  pfnUpdateSrcPtr                   ;addr of fn. to update src pointer
        localW  pfnGetAbsIndx                     ;addr of fn. to get next abs mode index
        localW  pfnDecideStrat                    ;decides strategy to adopt

cBegin

        cld

;/*
;** set up the address to the first byte of the screen, and adjust Y origin
;*/

rdb_dst_info:
        les     si,pddcDst                       ; ES:SI => the dest. ddc
        assumes es,nothing
        mov     si,es:[si].ddc_npsd              ; ES:SI => the dest. surface
        mov     ax,es:[si].sd_pBits.sel          ; the screen segment
        mov     selScreen,ax
        mov     cbDstScan,SCREEN_DSCAN

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

rdb_src_rect:
        xor     ax,ax                   ;the RLE starts from origin(0,0)
        mov     X,ax                    ;start X coordinate
        mov     Y,ax                    ;start Y coordinate
        mov     ax,xClipLft             ;the left edge
        add     ax,cxExt
        mov     xClipRgt,ax             ;the right edge
        mov     ax,yClipBot             ;the top edge
        add     ax,cyExt
        mov     yClipTop,ax             ;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:
        lds     si,lpRLEInfo                      ;ds:si points to rle info block
        assumes ds,nothing
        mov     ax,cxExt
        cmp     ax,ds:[si].bmp2_cx.lo   ;if the drawing width is not equal the
        setne   fbSurfaceFlags          ;bitmap width, indicate clipped rect
        .errnz  RLE_CLIPPED-00000001b
        mov     ax,cyExt
        cmp     ax,ds:[si].bmp2_cy.lo   ;if the drawing height is not equal the
        setne   al                      ;bitmap height, indicate clipped rect
        or      fbSurfaceFlags,al

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

rdb_4bpp_addr:
        cmp     ds:[si].bmp2_ulCompression.lo,BCA_RLE8
        je      short rdb_8bpp_addr              ;yes

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

        mov     ax,4                    ;we need only the encode nibble
        mov     bx,CodeOFFSET decide_strategy_4_bit
        mov     cx,CodeOFFSET update_src_ptr_4_bit
        mov     dx,CodeOFFSET get_next_abs_index_4_bit
        jmp     short rdb_have_addr

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

rdb_8bpp_addr:
        xor     ax,ax                   ;we need the full encode byte
        mov     bx,CodeOFFSET same_color_strategy
        mov     cx,CodeOFFSET update_src_ptr_8_bit
        mov     dx,CodeOFFSET ReadRLE

rdb_have_addr:
        mov     cEncodeShft,ax
        mov     [pfnDecideStrat],bx
        mov     [pfnUpdateSrcPtr],cx
        mov     [pfnGetAbsIndx],dx

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

        mov     ax,CodeOFFSET dev_set_pels_partial
        mov     pfnSetPartialPels,ax             ;set bits in partial bytes
        mov     ax,CodeOFFSET dev_set_pels_full
        mov     pfnSetFullPels,ax                ;set bits in complete bytes

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

rdb_cursor_exclude:
        mov     si,cxExt
        mov     di,cyExt
        dec     si                                ;Make extents inclusive of last point
        dec     di
        mov     cx,xDst                           ;Set left
        mov     dx,yDst                           ;Set top
        add     si,cx                             ;Set right
        add     di,dx                             ;Set bottom
        cCall   far_exclude                       ;Exclude the area from the screen

;/*
;** The Y 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 (xDst,yDst+cyExt-1). Set up the start X and Y coordinates.       ;
;*/

rdb_adjust_yDst:
        mov     ax,yDst                 ; get the Y origin (top scan)
        add     ax,cyExt                ; move it to the bottom of scan
        dec     ax
        mov     yDst,ax                 ; adjusted Y origin

;/*
;** set up the EGA registers for write mode 2                                  ;
;*/

rdb_init_board:
        mov     es,MyPtrCodeData                 ; Show color read mode to the
        assumes es,PtrData                       ;   EGA restoration code
        mov     dx,EGA_BASE + GRAF_ADDR          ; the GRX controller address

;/*
;** set up for READ 0/WRITE 2
;*/

        mov     ax,(M_COLOR_WRITE shl 8)+GRAF_MODE
        mov     es:shadowed_graf_mode.vvr_value,ah ; Must shadow this for state code
        out     dx,ax

        DevIODelay                                ;I/O delay destroys ax

;/*
;** set up the GRX address register with the index for the bit mask
;** register.
;*/

        mov     al,GRAF_BIT_MASK
        out     dx,al

        DevIODelay                                ;I/O delay destroys ax

        lds     si,lpRLEbits                      ;DS:SI points to rle buffer
        assumes ds,nothing
        mov     es,selScreen                     ;load surface selector into es
        assumes es,nothing
        mov     gs,lpClrTab.sel         ;load the color table selector
        assumes gs,nothing

;/*
;** The current position in the source is at (X,Y). The clipping rectangle is
;** from (xClipLft,yClipBot) to (xClipRgt,yClipTop)
;*/

rdb_set_decode_RLE:
        xor     ax,ax                   ;count is 1 byte
        mov     usNibbleToggle,ax       ;used for 4 bit RLEs
        cCall   ReadRLE                 ;get the next byte
        or      al,al                   ;is it 0
        jnz     short 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 
;*/

        cCall   ReadRLE                           ;get the next code byte
        cmp     al,3                              ;absolute mode ?
        jae     short rdb_set_absolute_mode       ;absolute mode of encoding
        cmp     al,2                              ;delta or segment cross
        je      short rdb_set_delta               ;yes
        or      al,al                             ;end of line?
        jz      short rdb_set_EOL                 ;yes
        jmp     short rdb_set_EORLE               ;end of encoding

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

rdb_set_absolute_mode:
        push    ax                      ;save the count of RLE
        test    fbSurfaceFlags,RLE_CLIPPED
        jz      short rdb_set_multipel_not_cliped

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

        cCall   set_get_segment         ;set up params for multipel segment

        ;   ax is pixels cliped on left
        ;   cx is pixels visible
        ;   bx is pixels cliped on right

        cCall   [pfnUpdateSrcPtr]           ;advance over left clipped pels
        jcxz    rdb_set_multipel_update_X_Y ;segment is clipped

;/*
;** At least a part of the segment is visible. (DI,Y) has the start coordinate,;
;** CX has the extent and DS:SI points to the index of the first pel in the    ;
;** visible part                                             ;
;*/

        push    bx                      ;save right clipped count
        cCall   set_multipel_segment    ;draws the segment, pels of diff color
        pop     ax                      ;restore right clip count and
        cCall   [pfnUpdateSrcPtr]    ;advance over clipped pels
        jmp     short rdb_set_multipel_update_X_Y

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

rdb_set_multipel_not_cliped:
        mov     di,X
        mov     cx,ax
        cCall   set_multipel_segment    ;draws the segment, pels of diff color

;/*
;** now update (X,Y) to account for the coding in the present RLE record.
;*/

rdb_set_multipel_update_X_Y:
        mov     ax,usNibbleToggle
        cCall   [pfnUpdateSrcPtr]
        pop     ax                      ;get back count of indices in record
        add     X,ax                    ;update the abscissa.

        test    si,1                    ;WORD align source pointer
        jz      rdb_set_decode_RLE
        cCall   ReadRLE
@@:
        jmp     short 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 happ-;
;**  -en:                                                            ;
;**       . 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    ax                               ;save count
        cCall   set_get_segment                  ;get start (x,y) in segment and count
        jcxz    rdb_set_onepel_update_X_Y        ;the whole segment is clipped

        cCall   [pfnDecideStrat]                 ;decide how to treat 4 bit encoding
        jnc     short 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.
;*/

        cCall   set_4bpp_onepel_segment          ;draw visible part of segment
        jmp     short rdb_set_onepel_update_X_Y

;/*
;** at least a part is visible. (AX,Y) has start coordinate, CX has extent,    ;
;** DS:SI points to the pel index to be used for each pel in the segment      ;
;*/

rdb_treat_as_encoded:
        cCall   set_onepel_segment               ;draw the visible part of the segment

;/*
;** update (X,Y) for start of next RLE record.
;*/

rdb_set_onepel_update_X_Y:
        cCall   ReadRLE                           ;advance source pointer
        pop     ax                                ;get back count of pels in RLE record
        add     X,ax                              ;move X, Y stays the same, SI is OK
        jmp     short rdb_set_decode_RLE          ;decode next RLE record.

;/*
;** we have reached a delta record
;*/

rdb_set_delta:
        cCall   ReadRLE                           ;get next byte
        xor     ah,ah                             ;zeroize
        mov     bx,ax                             ;save in bx
        cCall   ReadRLE                           ;fetch the next byte

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

        add     X,bx                    ;update X
        add     ax,Y                    ;update Y
        cmp     ax,yClipTop             ;do we pass the top edge?
        jae     short rdb_set_EORLE     ;yes, we are done. go to reset HW
        mov     Y,ax                    ;no, actually update Y
        jmp     rdb_set_decode_RLE      ;decode next  record

;/*
;** we have an end-of-line record. The action is to update Y and have the X    ;
;** 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     X,ax                    ;reset the abscissa
        inc     Y                       ;point to next scan
        mov     ax,yClipTop
        cmp     Y,ax                    ;do we pass the top edge?
        jb      rdb_set_decode_RLE      ;no, next RLE

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

rdb_set_EORLE:
        cCall   clean_up_before_exit_no_test;reinitialize the device
        cCall   far_unexclude                     ;allow cursor to be drawn

rdb_exit:
cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = ReadRLE   
;*
;* DESCRIPTION   = Reads a byte from a encoded RLE buffer.  This function 
;*                 will take care of segment crossing.                                        ;
;*                                                                            
;*                 Registers modified:                                                            
;*                   EAX ESI           
;*                                                                            
;* INPUT         = ESI --> RLE buffer
;*                 
;* OUTPUT        = AL  = Byte from buffer
;*                 ESI = Incremented     
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   ReadRLE,<NEAR,PUBLIC>

cBegin  <nogen>

        lodsb
        or      si, si
        jz      ReadRLENextSeg
        ret

ReadRLENextSeg:
        mov     si, ds
        add     si, DOSHUGEINCR
        mov     ds, si
        assumes ds,nothing
        xor     si, si
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = SetGetSegment   
;*
;* DESCRIPTION   = 
;*
;*     Gets a portion of the current segment from (X,Y) to (X+AX,Y) 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         = (X,Y)           - Start of segment for current RLE record 
;*                 (ClipLft,ClipTop) - Top left of the clip rectangle          
;*                 (ClipRgt,ClipBot) - Bottom right of the clip rectangle      
;*                 AX                - Number of pels in the segment
;*
;* OUTPUT        = CX - No of pels within clip rectangle, 0 => total clip 
;*                      (if CX=0, rest is not valid)                      
;*                 AX - No of pels clipped on left                        
;*                 BX - No of pels clipped on right                       
;*                 DI - Absisca of first visible pel in the segment       
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_get_segment,<NEAR,PUBLIC>

cBegin  <nogen>

        test    fbSurfaceFlags,RLE_CLIPPED
        jz      short set_get_segment_not_clipped
               
        mov     bx,X                              ;get left abscissa of segment
        mov     di,Y                              ;get ordinate
        xor     cx,cx                             ;assume total clip

;/*
;** if Y is > ClipTop (region-3) or < ClipBot (region_1) it is totall clipped
;*/

        cmp     di,yClipBot
        jb      short set_get_segment_end         ;segment is totaly clipped
        cmp     di,yClipTop
        jae     short set_get_segment_end         ;segment is totaly clipped

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

        cmp     bx,xClipRgt                       ;are we in region-2
        jae     short set_get_segment_end         ;yes. so nothing is visible
        push    ax                                ;save extent
        add     ax,bx                             ;ax has coord just past rt edge of seg
        cmp     ax,xClipLft                       ;are we in region-4
        pop     ax                                ;get back extent in ax
        jbe     short set_get_segment_end         ;another total clip case

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

        mov     cx,ax                             ;assume all is visible
        xor     ax,ax                             ;and nothing cliped on left

        cmp     bx,xClipLft                       ;do we start on the left edge
        je      short set_start_on_lft_edge       ;yes
        jg      short set_clip_type_2_or_3        ;cant be clip type 1 or 4

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

        sub     bx,xClipLft
        neg     bx                      ;no of pels clipped from left end

        sub     cx,bx                   ;bx less pixels to draw
        mov     ax,bx                   ;remeber left cliped pixels

set_start_on_lft_edge:
        mov     di,xClipLft             ;start on left edge
        mov     bx,cxExt                ;get extent not exceeding clip rect
        jmp     short set_get_segment_right_clip

set_clip_type_2_or_3:

        mov     di,bx                   ;start abscissa of visible segment
        sub     bx,xClipRgt             ;bx has left x of segment
        neg     bx

;/*
;**   cx is wanted extent
;**   bx is max alowed extent
;*/

set_get_segment_right_clip:
        cmp     cx,bx                   ;Are we clipped on the right?
        ja      short set_get_segment_right_clipped
        xor     bx,bx                   ;No, set right cliped to zero
        jmp     short set_get_segment_end

set_get_segment_right_clipped:
        sub     cx,bx                   ;We are right cliped, set...
        xchg    cx,bx

;/*
;**   ax is amount left clipped
;**   cx is clipped extent
;**   bx is amount right clipped
;*/
 
set_get_segment_end:
        ret

set_get_segment_not_clipped:
        mov     di,X
        mov     cx,ax
        xor     ax,ax
        xor     bx,bx
        ret

cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = set_multipel_segment:
;*
;* DESCRIPTION   = draws a segment onto the display surface, where the pels in the
;*                 segment are of different colors.          
;*                                                                            
;* INPUT         = (DI,Y)    -   start coordinate of the segment.    
;*                     CX    -   no pels in the segment    
;*                  DS:SI    -   pointer to the index for the first pel in the segment
;*                 
;* OUTPUT        = None
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_multipel_segment,<NEAR,PUBLIC>

cBegin  <nogen>
        
;/*
;** calculate the start offset in the surface and the the bit mask and start
;** byte mask
;*/

        cCall   set_get_bit_mask

;/*
;** ES:DI points to byte on destination surface to hold the first bit. AH has
;** the bit mask.
;*/

        mov     bx,lpClrTab.off                  ;address of the translation table
        mov     dx,EGA_BASE+GRAF_DATA            ;address of bitmask register

set_multipel_loop:
        
        cCall   [pfnGetAbsIndx]                   ;get next pel index
        xlat    gs:[bx]                           ;get the color value for the index

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

        cCall   [pfnSetPartialPels]               ;set the pels
        ror     ah,1                              ;update mask to next pel
        adc     di,0                              ;update byte if wrap around
        loop    set_multipel_loop
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = set_onepelsegment
;*
;* DESCRIPTION   = Draws a segment onto the display surface, where the pels  
;*                 in the segment are all of the same color.                 
;*
;* INPUT         = (DI,Y) - Start coordinate of the segment.      
;*                 CX     - No pels in the segment                
;*                 DS:SI  - Pointer to the index for the pel color
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_onepel_segment,<NEAR,PUBLIC>

cBegin  <nogen>
        
;/*
;** calculate the start offset in the surface and the the bit mask and start
;** byte mask
;*/

        cCall   set_get_byte_mask

;/*
;** ES:DI points to byte on destination surface to hold the first bit.
;** BL has the first byte mask, BH has the last byte mask and CX has the inner
;** byte count.
;*/

        mov     dx,bx                             ;save masks
        mov     bx,lpClrTab.off                  ;address of the translation table
        xchg    cx,cEncodeShft
        mov     al,ds:[si]              ;fetch the encode byte
        shr     al,cl                             ;if 4bpp, left nibble of interest
        xchg    cx,cEncodeShft
        xlat    gs:[bx]                           ;get the color value for the index
        mov     bx,dx                             ;get back masks
        mov     dx,EGA_BASE+GRAF_DATA   ;address of bitmask register

set_onepel_first:

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

        mov     ah,bl                             ;get first byte mask
        jcxz    set_onepel_last                   ;do the last byte only.

;/*
;** do the first byte
;** ES:DI-> destination byte. AH has byte mask. AL has pel color
;*/

        cCall   [pfnSetPartialPels]               ;set the pels for partial byte
        inc     di                                ;point to next byte
        dec     cx                                ;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
        jcxz    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:
        cCall   [pfnSetFullPels]                  ;do a complete byte
        inc     di                                ;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
        cCall   [pfnSetPartialPels]              ;set pels in the last byte
        ret

cEnd    <nogen>
 
;/***************************************************************************
;*
;* FUNCTION NAME = set_4bpp_onepel_segment
;*
;* 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         = (DI,lY) - Start coordinate of the segment.          
;*                 CX      - No pels in the segment.                   
;*                 DS:SI   - Pointer to the index for the 2 pel colors.
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_4bpp_onepel_segment,<NEAR,PUBLIC>

cBegin  <nogen>

        pushem  di,cx,si                          ;save these  values
        cCall   set_onepel_segment                ;do the entire thing with one color
        popem   di,cx,si                          ;gat back saved values
        mov     ax,[pfnSetFullPels]               ;get the full pel address
        push    ax                                ;save it
        mov     ax,[pfnSetPartialPels]            ;get the partial pel address
        mov     [pfnSetFullPels],ax               ;will need masking all through

        
;/*
;** calculate the start offset in the surface and the the bit mask and start
;** byte mask
;*/

        cCall   set_get_byte_mask

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

        pushem  bx,cx                             ;save
        mov     cx,X                              ;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   bx,cx                             ;get back actual masks
        and     bx,ax                             ;update masks
        mov     bspecial_4bpp_mask,al             ;save it

;/*
;** ES:DI points to byte on destination surface to hold the first bit.
;** BL has the first byte mask, BH has the last byte mask and CX has the inner
;** byte count.
;*/

        mov     dx,bx                             ;save masks
        mov     bx,lpClrTab.off                   ;address of the translation table
        mov     al,[si]                           ;get the index
        and     al,0fh                            ;interested in second nibble
        xlat    gs:[bx]                           ;get the color value for the index
        mov     bx,dx                             ;get back masks
        mov     dx,EGA_BASE+GRAF_DATA             ;address of bitmask register

set_4bpp_onepel_first:

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

        mov     ah,bl                             ;get first byte mask
        jcxz    set_4bpp_onepel_last              ;do the last byte only.

;/*
;** do the first byte
;** ES:DI-> destination byte. AH has byte mask. AL has pel color
;*/

        cCall   [pfnSetPartialPels]               ;set the pels for partial byte
        inc     di                                ;point to next byte
        dec     cx                                ;first pel was incuded in count

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

        mov     ah,bspecial_4bpp_mask             ;all bits enabled
        jcxz    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:
        cCall   [pfnSetFullPels]                  ;do a complete byte
        inc     di                                ;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
        cCall   [pfnSetPartialPels]               ;set pels in the last byte

        pop     ax                                ;got back the full pel address
        mov     [pfnSetFullPels],ax               ;restore it
        ret

cEnd    <nogen>

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

        assumes ds,nothing
        assumes es,nothing

cProc   dev_set_pels_partial,<NEAR,PUBLIC>

cBegin  <nogen>

        push    ax
        xchg    ah,al                             ;get mask byte into AL
        out     dx,al                             ;the mask is set
        xchg    es:[di],ah                        ;set the pel colors
        pop     ax
        ret

cEnd    <nogen>


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

        assumes ds,nothing
        assumes es,nothing

cProc   dev_set_pels_full,<NEAR,PUBLIC>

cBegin  <nogen>

        push    ax
        xchg    es:[di],al                        ;set the pel colors
        pop     ax
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = set_get_bit_mask
;*
;* DESCRIPTION   = computes pointer to start byte on destination surface and bit mask 
;*                 for the first bit in the mask.
;*
;* INPUT         = (DI,Y)  - start point on surface               ;
;*                     CX  - no of pels in the segment        ;
;*
;*                  Registers destroyed:                                            ;
;*                                  AL,BX,DX                                 ;
;*
;* OUTPUT        = ES:DI   - strat byte on the surface.        ;
;*                    AH   - bitmask                       ;
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_get_bit_mask,<NEAR,PUBLIC>

cBegin  <nogen>

        cCall   set_start_offset
        
;returns start offset in ES:DI, StartX & 00000111b in dl

        push    cx                                ;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     cx                                ;restore
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = set_get_byte_mask
;*
;* 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         = (DI,Y)  - Start point on surface.
;*                     CX  - No of pels in the segment.
;*                 
;* OUTPUT        = ES:DI  --> Start byte on the surface.
;*                 BL     -  First byte mask.
;*                 BH     -  Last byte mask.
;*                 CX     -  Number of intermediate bytes (may be 0)
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   set_get_byte_mask,<NEAR,PUBLIC>

cBegin  <nogen>

        cCall   set_start_offset
        
;/*
;** rets ES:DI pointing to start offset and StrtX & 00000111b in dl
;*/

        mov     ax,cx                             ;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     cx,ax                             ;get back pel count
        dec     cx                                ;exclude last pel
        mov     ax,cx                             
        shr     ax,3                              ;get number of complete byte in run
        and     cx,00000111b                      ;and the number of bits left
        mov     bh,bl                             ;save start mask
        ror     bl,cl                             ;get end mask
        mov     cx,ax                             ;number of bytes
        cmp     bh,bl                             ;test for wrap around
        adc     cx,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

cEnd    <nogen>

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

        assumes ds,nothing
        assumes es,nothing

cProc   set_start_offset,<NEAR,PUBLIC>

cBegin  <nogen>

        mov     ax,yDst
        sub     ax,Y
        add     ax,yClipBot             ; current scan
        mul     cbDstScan               ; no of bytes in a scan
        assert  dx,E,0
        sub     di,xClipLft
        add     di,xDst                 ;get the x origin
        mov     dx,di                   ;get start x
        shr     di,3                    ;get no of complete bytes
        add     di,ax                   ;di has correct offset
        and     dl,00000111b            ;get no of bits before start bit
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = update_src_ptr_4_bit
;*
;* 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         = SI --> Start of last record.                  
;*                 AX  =  Number of pels encoded in last record. 
;*
;* OUTPUT        = SI --> Start of next record.
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   update_src_ptr_4_bit,<NEAR,PUBLIC>

cBegin  <nogen>

        add     ax,usNibbleToggle       ;handle odd nibble dudes
        mov     usNibbleToggle,ax       ;save low bit in usNibbleToggle
        and     usNibbleToggle,1
        shr     ax,1                    ;convert nibble count to bytes

update_src_ptr_8_bit:
        add     si,ax                   ;point to next RLE record
        jnc     short @f                ;advance segment if overflow
        mov     ax, ds
        add     ax, DOSHUGEINCR
        mov     ds, ax
        assumes ds,nothing
@@:
        ret

cEnd    <nogen>

;/***************************************************************************
;*
;* FUNCTION NAME = get_next_abs_index_4_bit
;*
;* 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
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   get_next_abs_index_4_bit,<NEAR,PUBLIC>

cBegin  <nogen>

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

        cCall   ReadRLE
        and     al,00001111b                      ;retain the left nibble.
        ret

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

        mov     al,[si]                           ;get the current byte
        shr     al,4                              ;get the left nibble into the right
        ret

cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = decide_strategy_4_bit
;*
;* 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         = SI   --> Encode records index byte.
;*                 AX   --- 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
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   decide_strategy_4_bit,<NEAR,PUBLIC>

cBegin  <nogen>

        push    ax                                ;save ax
        mov     al,[si]                           ;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     ax                                ;restore ax
        je      same_color_strategy               ;stretch is of same color
        stc                                       ;set carry flag, different color
        ret

same_color_strategy:
        clc                                       ;reset carry
        ret

cEnd    <nogen>

sEnd    Code

        public  rdb_dst_info
        public  rdb_src_rect
        public  rdb_clip_test
        public  rdb_4bpp_addr
        public  rdb_8bpp_addr
        public  rdb_have_addr
        public  rdb_cursor_exclude
        public  rdb_adjust_yDst
        public  rdb_init_board
        public  rdb_set_decode_RLE
        public  rdb_set_absolute_mode
        public  rdb_set_multipel_not_cliped
        public  rdb_set_multipel_update_X_Y
        public  rdb_set_encoded_run
        public  rdb_treat_as_encoded
        public  rdb_set_onepel_update_X_Y
        public  rdb_set_delta
        public  rdb_set_EOL
        public  rdb_set_EORLE
        public  rdb_exit

end
