;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
       page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = SCANLN2.ASM
;*
;* DESCRIPTIVE NAME = Part 2 of Scanline 
;*
;*
;* VERSION      V2.0
;*
;* DATE         09/22/88
;*
;* DESCRIPTION  This file contains the second part of scanline, which was
;*              too big.
;*
;* FUNCTIONS    transp_dpna 
;*              transp_dpa
;*              transp_ddx
;*              transp_pn
;*              transp_p
;*              transp_dpon
;*              transp_pdna
;*              transp_dpxn
;*              transp_dpx
;*              transp_dn
;*              transp_pdno
;*              transp_dpan
;*              transp_dpno
;*              transp_dpo
;*              transp_ddxn
;*              transp_d
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   04/13/88                     Bob Grudem [bobgru] Add special case check for
;*                                fg mix = D, bk mix = P (cures the problem of
;*                                black background color causing brush to become
;*                                solid black).
;*                                Bob Grudem [bobgru] Add in vertical rotation
;*                                (i.e.  (brush origin y coord) mod 8).
;*   04/16/88                     Bob Grudem [bobgru] Partially rewrote to
;*                                properly handle background mix modes.
;*   09/22/88                     Author: Lee A. Newberg  [leen]
;*   10/07/88                     Mitchell McLain [gssc!mmm] Fixed sense of
;*                                test/jmp combination when looking at
;*                                BM_BACK_MIX.
;*   12/22/89                     Viroon Touranachun [viroont] Modified it to
;*                                work with both background and foregorund
;*                                mixmodes.
;*                                Viroon Touranachun [viroont] Allowed it to
;*                                initialize upto 16 background mixmodes.
;*****************************************************************************/


;/*
;** The high byte in the following constant is the mask to determine the
;** pixel position within a data item.  The low byte is the shift count
;** for division, to determine which data item a pixel is in.
;*/

PSL_WORD_FETCH  equ     0F04h


;/*
;**  The following table is used to determine which drawing function
;**  should be invoked for the inner loop bytes of the interval
;** 
;**  The table is indexed as follows:
;** 
;**        000 x rrrr
;** 
;**            |  |
;**            |   --------  raster op index
;**            |
;**             ----  transparent pixels if 1
;** 
;** 
;**  The table can also be used to determine the drawing function
;**  for the first and last bytes of the intervals by faking
;**  transparent mode.
;** 
;**  All entries are the offset from the start of the raster
;**  op drawing routines, which allows the table to be stored
;**  as bytes instaed of words, saving 32 bytes.
;*/


proc_addr_tbl   label byte

        db      transp_ddx  - rops_start
        db      transp_dpon - rops_start
        db      transp_dpna - rops_start
        db      transp_pn   - rops_start
        db      transp_pdna - rops_start
        db      transp_dn   - rops_start
        db      transp_dpx  - rops_start
        db      transp_dpan - rops_start
        db      transp_dpa  - rops_start
        db      transp_dpxn - rops_start
        db      transp_d    - rops_start
        db      transp_dpno - rops_start
        db      transp_p    - rops_start
        db      transp_pdno - rops_start
        db      transp_dpo  - rops_start
        db      transp_ddxn - rops_start

;/*
;** The following table is used to map raster ops when the pattern
;** is all 1's or 0's.  All the rops can be mapped into one of
;** the following:
;**
;**       DDxn    DDx     Dn      D
;**
;**
;** based on the following results:
;**
;**
;**             Color     Result                Color     Result
;**
;**       DDx     0         0             DPa     0         0
;**               1         0                     1        dest
;**
;**       DPon    0       ~dest           DPxn    0       ~dest
;**               1         0                     1        dest
;**
;**       DPna    0        dest           D       0        dest
;**               1         0                     1        dest
;**
;**       Pn      0         1             DPno    0         1
;**               1         0                     1        dest
;**
;**       PDna    0         0             P       0         0
;**               1       ~dest                   1         1
;**
;**       Dn      0       ~dest           PDno    0       ~dest
;**               1       ~dest                   1         1
;**
;**       DPx     0        dest           DPo     0        dest
;**               1       ~dest                   1         1
;**
;**       DPan    0         1             DDxn    0         1
;**               1       ~dest                   1         1
;**
;**
;**
;** The table is indexed as follows:
;**
;**       000 rrrr c
;**
;**            |   |
;**            |   +----  color 0 = 1s, 1 = 0's
;**            |
;**            +--------  raster op index
;*/

map_1s_0s       label   byte
        db      ROP_DDX                 ;DDx    with all 0s
        db      ROP_DDX                 ;DDx    with all 1s
        db      ROP_DN                  ;DPon   with all 0s
        db      ROP_DDX                 ;DPon   with all 1s
        db      ROP_D                   ;DPna   with all 0s
        db      ROP_DDX                 ;DPna   with all 1s
        db      ROP_DDXN                ;Pn     with all 0s
        db      ROP_DDX                 ;Pn     with all 1s
        db      ROP_DDX                 ;PDna   with all 0s
        db      ROP_DN                  ;PDna   with all 1s
        db      ROP_DN                  ;Dn     with all 0s
        db      ROP_DN                  ;Dn     with all 1s
        db      ROP_D                   ;DPx    with all 0s
        db      ROP_DN                  ;DPx    with all 1s
        db      ROP_DDXN                ;DPan   with all 0s
        db      ROP_DN                  ;DPan   with all 1s
        db      ROP_DDX                 ;DPa    with all 0s
        db      ROP_D                   ;DPa    with all 1s
        db      ROP_DN                  ;DPxn   with all 0s
        db      ROP_D                   ;DPxn   with all 1s
        db      ROP_D                   ;D      with all 0s
        db      ROP_D                   ;D      with all 1s
        db      ROP_DDXN                ;DPno   with all 0s
        db      ROP_D                   ;DPno   with all 1s
        db      ROP_DDX                 ;P      with all 0s
        db      ROP_DDXN                ;P      with all 1s
        db      ROP_DN                  ;PDno   with all 0s
        db      ROP_DDXN                ;PDno   with all 1s
        db      ROP_D                   ;DPo    with all 0s
        db      ROP_DDXN                ;DPo    with all 1s
        db      ROP_DDXN                ;DDxn   with all 0s
        db      ROP_DDXN                ;DDxn   with all 1s

;/*
;**       another_tbl contains various flags for the different raster ops
;*/

another_tbl     label   byte

        db      MY_SINGLE_OK+NO_OBJECT+STOSB_OK                         ;DDx
        db      ALT_NEG_PATTERN                                         ;DPon
        db      MY_SINGLE_OK+ALT_NEG_PATTERN                            ;DPna
        db      MY_SINGLE_OK+STOSB_OK+NEG_PATTERN+ALT_NEG_PATTERN       ;Pn
        db      0                                                       ;PDna
        db      MY_SINGLE_OK+NO_OBJECT                                  ;Dn
        db      MY_SINGLE_OK                                            ;DPx
        db      0                                                       ;DPan
        db      MY_SINGLE_OK                                            ;DPa
        db      MY_SINGLE_OK+ALT_NEG_PATTERN                            ;DPxn
        db      MY_SINGLE_OK+NO_OBJECT+STOSB_OK                         ;D
        db      MY_SINGLE_OK+ALT_NEG_PATTERN                            ;DPno
        db      MY_SINGLE_OK+STOSB_OK                                   ;P
        db      ALT_NEG_PATTERN                                         ;PDno
        db      MY_SINGLE_OK                                            ;DPo
        db      MY_SINGLE_OK+NO_OBJECT+STOSB_OK+ALT_NEG_PATTERN         ;DDxn

        page

;/***************************************************************************
;*
;* FUNCTION NAME = comp_scan
;*
;* DESCRIPTION   = The starting Y coordinate will be computed, and the device   
;*                 specific parameters fetched and saved.  If this is the       
;*                 display, the area of the screen where the drawing will occur 
;*                 will be excluded.  The Y of the scan will be saved incase it 
;*                 is required for a brush.  The offset of the first point will 
;*                 be saved for the main loop code, and the points count        
;*                 decremented to account for the point containing the starting 
;*                 Y. The default looping logic and plane increment values will 
;*                 also be set.                                                 
;*
;*                 Registers Preserved:       
;*                       BP                         
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,SI,DI,DS,FLAGS 
;*                 Calls:
;*                       exclude
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = CL = loop_control flag 
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    comp_scan,FAR
cBegin  <nogen>

        mov     loc_clean_up,CU_NONE    ;Show no clean-up needed
        mov     ax,loc_saved_y          ; I set saved_y earlier  --Wes

        lds     si,arg_lp_ddc
        assumes ds,nothing
        ddc?    si

        xor     dx,dx                   ;Set segment bias to 0
        mov     si,[si].ddc_npsd        ;get the surface definition from ddc
        test    [si].sd_fb,SD_HUGE
        jz      cs_for_bitmap_40


;/*
;** This is a huge bitmap.  Compute which segment the Y coordinate
;** is in.  Assuming that no huge bitmap will be bigger than two
;** or three segments, iteratively computing the value would be
;** faster than a divide, especially if Y is in the first segment
;** (which would always be the case for a huge color bitmap that
;** didn't have planes >64K).
;*/

        mov     bx,[si].sd_cySeg        ;Get # scans per segment
        mov     cx,DOSHUGEINCR

cs_for_bitmap_30:
        add     dx,cx                   ;Show in next segment
        sub     ax,bx                   ;See if in this segment
        jnc     cs_for_bitmap_30        ;Not in current segment, try next
        add     ax,bx                   ;Restore correct Y
        sub     dx,cx                   ;Show correct segment

;/*
;** This is a memory DC.  If this is a monochrome memory DC, set up
;** the inner loop so that it will terminate after one time through
;** and set the color to be equal to the mono bit in the physical
;** color.  If it is color, set up the inner loop for all planes,
;** same as for the display.
;**
;** Also handle modifying Y for huge bitmaps if necessary.
;**
;**
;**       Currently:
;**               AX     =  Y coordinate
;**               DX     =  Segment bias for huge bitmaps
;**               DS:SI --> display surface structure
;**               CH     =  Huge bitmap flag
;**                         (0 = small bitmap)
;*/

cs_for_bitmap_40:
        mov     di,[si].sd_cbScan       ;Get index to next plane
        mov     bx,di                   ;  (and Y multiplier)
        add     dx,[si].sd_pBits.sel    ;Compute segment of the bits
        mov     loc_scan_start.sel,dx   ;Save segment of the scan
        mul     bx                      ;Compute start of scan
        add     ax,[si].sd_pBits.off    


;/*
;**  The looping logic always adds in next_scan at the start of the loop.
;**  This is countered now by biasing the scan lines start address by this
;**  amount.
;** 
;**  This is actually a remnant of the original EGA code.  It should be
;**  removed from this code, because there is no longer any looping going
;**  on.  Removal requires modification to comp_interval, which appears to
;**  be the only code that directly uses this value.
;*/

        sub     ax,di                   ;Bias offset for looping logic
        mov     loc_scan_start.lo,ax    ;Save address of scan


comp_scan_save:
        mov     loc_some_flags,INDEX_MONO ;Move mono flag into loc_some_flags
        mov     loc_next_plane,di       ;Save index to next plane
        ret
cEnd    <nogen>
        page

;/***************************************************************************
;*
;* FUNCTION NAME = get_fill_data
;*
;* DESCRIPTION   = Get All Data Required For Filling The Scan
;*
;*                 Registers Preserved:       
;*                       ES,BP
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,SI,DI,DS,FLAGS 
;*                 Calls:
;*                       set_procs
;*
;* INPUT         = CL = loop_control value 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = 'C' clear if successful  
;* RETURN-ERROR  = 'C' set if to abort 
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    get_fill_data,FAR
cBegin  <nogen>

        lds     di,arg_lp_ddc
        assumes ds,nothing
        ddc?    di

;/*
;** Combine the pattern foreground color and accelerator flags (we only
;** use the PA_SINGLE_CLR flag). Save local copy of the transparency mask
;** for the correct scan of the brush.
;*/

        xor     cx,cx
        mov     al,[di].ddc_pa.pa_ba.ba_ipc.ipc_bStatus
        test    loc_some_flags,INDEX_BKGRD
        jz      gfd_brush_acc
        mov     al,[di].ddc_pa.pa_ba.ba_ipcBack.ipc_bStatus
        dec     cx

gfd_brush_acc:
        and     al,(MONO_BIT shr 8) or (ONES_OR_ZEROS shr 8)
        mov     ah,[di].ddc_pa.pa_fb
        and     ah,PA_SINGLE_CLR
        .errnz  PA_SINGLE_CLR - 10000000b
        or      ah,al
        mov     bx,loc_bm_height                  ;Correct for the fact we align top left
        neg     bx                                ;  instead of bottom left
        add     bl,byte ptr [di].ddc_pa.pa_ptsOrg.pts_y
        add     bl,byte ptr loc_saved_y           ;The current Y scan dictates which
        and     bx,00000111b                      ;  scan of brush to use
        mov     al,[di].[bx].ddc_pa.pa_abMask
        xor     al,cl                             ;Must invert mask if background
        mov     dl,al
        mov     dh,al
        mov     loc_transparency_mask,dx


;/*
;** Since we are now in a very good occasion (CX = FFFFh if background, and
;** 0 otherwise), we can calculate the offset of the first control element
;** right here. Specifically, we need:
;**
;**       loc_proc_offset = 0                         ;for foreground mixmode
;**                       = SIZE cntrl_blk            ;for background mixmode
;*/

gfd_calc_offset:

ifdef FIREWALLS
        push    ax
        lea     ax,loc_processors
        lea     dx,loc_bk_processors
        sub     ax,dx
        sub     ax,(SIZE cntrl_blk)
        jz      @F
        rip     text,<SCANLINE: Non adjacent processor blocks>
@@:
        pop     ax
endif;FIREWALLS

        and     cx,-(SIZE cntrl_blk)
        mov     loc_proc_offset,cx

;/*
;**  Two conditions allow us to exit early:
;** 
;**     If the transparency mask is all 1's, and the foreground mix mode
;**     is D (i.e. NOP), then there's no work to do on this scanline.
;** 
;**     If the background is LeaveAlone, but the transparency mask is all
;**     0's, then neither any foreground nor background bits are visible,
;**     so signal the caller that this scanline is done.
;** 
;**  Currently:
;**        AL = transparency mask
;**        AH = brush accelerators
;**        BX = current scan of brush
;*/

        cmp     al,0FFh
        jne     gfd_check_background_mix
        cmp     byte ptr loc_raster_op,ROP_D
        je      gfd_exit_with_carry

gfd_check_background_mix:
        or      al,al
        jnz     gfd_gather_colors

gfd_exit_with_carry:
        stc
        jmp     gfd_exit

;/*
;**  Gather up the color bits for the current scan of the brush.  If
;**  we're writing to a mono bitmap, only bother with the mono plane's
;**  bits.
;** 
;**  Four of the Rops do not require an object (D, Dn, DDx, DDxn).
;**  If the current rop is one of these, then we want to show
;**  that the pattern is solid so that special case code can
;**  kick in regardless of what pattern was given.
;** 
;**  Currently:
;**        AL = transparency mask
;**        AH = brush accelerator flags
;**        BX = current scan of brush
;**        DI = DDC
;*/

gfd_gather_colors:


        mov     dl,[di].[bx].ddc_pa.pa_abMono
        mov     si,loc_raster_op
        mov     ah,another_tbl[si]      ;These flags will be useful
        test    ah,NO_OBJECT            ;Is an object required?
        jz      gfd_we_have_colors      ;If yes, not one of the rops


;/*
;** If we have an object-less rop, set foreground pattern bits to black.
;** The rop may cause the foreground bits to be inverted to white.
;*/

        not     al
        and     dl,al

gfd_we_have_colors:


;/*
;**  As much device independent preprocessing has been done
;**  as possible.  Now get into the specifics of mono/color/ega.
;** 
;**  Currently:
;**        AL = brush accelerator flags
;**        AH = another_tbl[SI]
;**        BL = color for C3
;**        CL = color for C2
;**        DH = color for C1
;**        DL = color for C0/mono
;**        SI = raster op
;*/

;/*
;** Set up the process addresses for a mono bitmap.
;*/

        ror     ah,1                    ;Set AH = 0FFh if to invert the pattern
        sbb     ah,ah
        .errnz  ALT_NEG_PATTERN - 00000001b
        and     ah,byte ptr loc_transparency_mask;Only want to invert foreground bits
        mov     al,80h                  ;D7 = 1 to show nop
        lea     si,loc_processors       ;--> first control element
        add     si,loc_proc_offset
        mov     ch,dl                   ;Set up for C0 or mono color

        call    far ptr set_procs
        add     al,al                   ;Set 'C' if operation can be skipped
gfd_exit:
        ret
cEnd    <nogen>

        page

;/***************************************************************************
;*
;* FUNCTION NAME = set_procs
;*
;* DESCRIPTION   = The processor addresses for performing the raster operation  
;*                 with the current pattern and transparent pixels (if any) are
;*                 set in the given structure
;*
;*                 Registers Preserved:       
;*                       CL,DX,DS,ES    
;*                 Registers Destroyed:             
;*                       CH,BX,DI,FLAGS 
;*
;* INPUT         = CH     =  pattern for this plane
;*                 AL:D7  =  NOP flag
;*                 AH     =  0FF if to invert pattern, 00 otherwise
;*                 SS:SI --> proc_blk structure where info is to be stored
;* OUTPUT        = AL     =  0 if function isn't ROP_D, else unaltered
;*                 AH     =  0FF if to invert pattern, 00 otherwise
;*                 SS:SI --> next proc_blk structure
;*
;* RETURN-NORMAL = 'C' clear if successful  
;* RETURN-ERROR  = 'C' set if to abort 
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    set_procs,FAR
cBegin  <nogen>

        xor     ch,ah                   ;Invert pattern if needed
        mov     ss:[si].pattern,ch
        xor     ch,ah
        mov     bx,loc_raster_op        ;Need raster op index
        inc     ch                      ;Colors of all 1s or 0s can be
        cmp     ch,1                    ;  special cased
        ja      set_procs_no_map        ;Not 00 or FF
        sub     ch,1                    ;Set 'C' = color (1 or 0)
        rcl     bx,1
        mov     bl,map_1s_0s[bx]        ;BL = mapped rop

set_procs_no_map:
        mov     ss:[si].nop_flag,NF_NOP ;assume nop
        cmp     bl,ROP_D                ;If rop is ROP_D
        jne     set_procs_not_nop       ;  and no background bits are visible
        cmp     loc_transparency_mask,0FFh  ;  then we're done
        je      set_procs_exit

set_procs_not_nop:
        xchg    ax,di                   ;Must save AH
        xor     ax,ax
        mov     al,proc_addr_tbl[bx]    ;Compute rop's address and save it
        add     ax,CodeOFFSET rops_start
        mov     ss:[si].inner_loop_proc,ax
        mov     ss:[si].first_last_proc,ax
        xchg    ax,di
        xor     al,al                   ;Clear D7 to show operation occured
        and     ss:[si].nop_flag,not NF_NOP ;assumption was wrong

set_procs_exit:
        add     si,size cntrl_blk       ;--> next control block structure
        ret
cEnd    <nogen>
        page

;/*
;** BM The following two bit mask tables are used for fetching
;** the first and last byte used-bits bitmask.
;**
;** So I lied [Wes, presumably].  I no longer use the tables, I create the
;** masks on the fly.  I'll leave them here so you can get the idea of
;** what I'm trying to do later in the code.
;*/

bit_mask_tbl_left label word

;/*
;** Masks for leftmost byte
;*/

;/*
;**     dw      1111111111111111B
;**     dw      0111111111111111b
;**     dw      0011111111111111b
;**     dw      0001111111111111b
;**     dw      0000111111111111b
;**     dw      0000011111111111b
;**     dw      0000001111111111b
;**     dw      0000000111111111b
;**     dw      0000000011111111b
;**     dw      0000000001111111b
;**     dw      0000000000111111b
;**     dw      0000000000011111b
;**     dw      0000000000001111b
;**     dw      0000000000000111b
;**     dw      0000000000000011b
;**     dw      0000000000000001b
;*/


bit_mask_tbl_right label word

;/*
;**Masks for rightmost byte
;*/

;/*
;**     dw      1000000000000000B       
;**     dw      1100000000000000B
;**     dw      1110000000000000B
;**     dw      1111000000000000B
;**     dw      1111100000000000B
;**     dw      1111110000000000B
;**     dw      1111111000000000B
;**     dw      1111111100000000B
;**     dw      1111111110000000B
;**     dw      1111111111000000B
;**     dw      1111111111100000B
;**     dw      1111111111110000B
;**     dw      1111111111111000B
;**     dw      1111111111111100B
;**     dw      1111111111111110B
;**     dw      1111111111111111B
;*/
        page

;/***************************************************************************
;*
;* FUNCTION NAME = comp_interval
;*
;* DESCRIPTION   = The next interval to be filled will be computed.  A first  
;*                 mask and a last mask will be calculated, and possibly      
;*                 combined into the inner loop count.  If no first byte      
;*                 exists, the start address will be incremented by the size  
;*                 (byte/word) to adjust for it.                              
;*
;*                 Registers Preserved:       
;*                       ES,BP                   
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,SI,DI,FLAGS 
;*
;* INPUT         = CH     =  pattern for this plane
;*                 AL:D7  =  NOP flag
;*                 AH     =  0FF if to invert pattern, 00 otherwise
;*                 SS:SI --> proc_blk structure where info is to be stored
;* OUTPUT        = AL     =  0 if function isn't ROP_D, else unaltered
;*                 AH     =  0FF if to invert pattern, 00 otherwise
;*                 SS:SI --> next proc_blk structure
;*
;* RETURN-NORMAL = 'C' clear             
;*                 DS:DI --> first point
;* RETURN-ERROR  = 'C' if no more intervals 
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

comp_interval_null:
        stc
        retf

define_frame    comp_interval,FAR
cBegin  <nogen>

        lea     si,loc_points           ;--> the scanline pairs
        mov     dx,ss:[si].pts_x        ;Get start & end coordinates
        mov     ax,ss:[si].pts_y

        mov     bx,ax                   ;Compute extent of interval
        sub     bx,dx
        jle     comp_interval_null      ;Null interval, skip it
        dec     bx                      ;Make interval inclusive



;/*
;** Compute the starting word address of this interval.  PSL_WORD_FETCH
;** defines the shift count required for words.
;*/

        mov     cx,PSL_WORD_FETCH       ;Get mask/shift counts
        mov     di,dx                   ;Don't destroy starting x
        shr     di,cl                   ; 
        mov     ds,loc_scan_start.hi    ;Set segment of first byte/word
        assumes ds,nothing
;/*
;** We now have to determine how many bits will be affected,
;** and how they are aligned within the bytes.
;**
;** (left_x MOD word_size) will give us the starting pixel
;** within the left byte/word.  Adding the inclusive extent
;** of the interval to left_x MOD word_size) and taking the
;** result MOD word_size will give us the last pixel affected
;** in the last byte/word.        These pixel indexes (0:7 for bytes,
;** 0:15 for words), can be used to create the first and last
;** altered bits mask.
;**
;**
;** To compute the number of bytes/words in the inner loop,
;** use the second calculation above
;**
;**       (left_x MOD word_size) + inclusive_extent
;**
;** and divide it by the word size (8/16).  This gives you
;** the following:
;**
;**
;**           1)  If the result is 0, then only one destination
;**               byte/word is being altered.  In this case, the
;**               start & ending masks should be ANDed together,
;**               the innerloop count set to zero, and last_mask
;**               set to to all 0's (don't alter any bits).
;**
;**                       |      x x x x x|               |
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 3,  extent-1 = 4
;**                       3+7 DIV 8 = 0, only altering one byte
;**
;**
;**
;**           2)  If the result is 1, then only two bytes/words
;**               will be altered.  In this case, the start and
;**               ending masks are valid, and all that needs to
;**               be done is set the innerloop count to 0.
;**
;**                       |  x x x x x x x|x x x x x x x|
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 1,  extent-1 = 14
;**                       3+14 DIV 8 = 1.  There is a first and last
;**                       byte but no innerloop count
;**
;**
;**
;**           3)  If the result is > 1, then some number of entire
;**               bytes/words will be altered by the innerloop.  In
;**               this case the number of innerloop bytes/words will
;**               be the result - 1.
;**
;**                       |              x|x x x x x x x x|x
;**                       |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
;**                        0 1 2 3 4 5 6 7
;**
;**                       start MOD 8 = 7,  extent-1 = 9
;**                       7+9  DIV 8 = 2.  There is a first and last
;**                       byte and an innerloop count of 1 (result - 1)
;*/


;/*
;** Compute the starting bit position on the left and the ending
;** bit position on the right
;*/


        and     dl,ch                   ;Compute bit index for left side
        xor     dh,dh
        add     bx,dx                   ;Compute bit index for right side
        mov     si,bx                   ;(save for inner loop count)
        and     bl,ch


;/*
;** Turn the bit index within the word (or byte) into a mask.
;** We are basically performing a table lookup into the two
;** tables bit_mask_tbl_right and bit_mask_tbl_left.  However,
;** by doing either arithmetic shifts or logical shifts, we
;** can create the masks and save a lot of table space.
;*/


        mov     ch,cl                   ;Save  byte/word shift count
        mov     cl,dl                   ;Compute left side altered bits mask
        mov     ax,0FFFFh
        mov     dx,ax                   ;Need this here later
        shr     ax,cl                   ;Compute right side altered bits mask
        mov     cl,bl
        mov     bx,8000h
        sar     bx,cl
        mov     cl,ch                   ;Restore word shift count

        shl     di,1                    ;Turn word offset into byte offset
        add     di,loc_scan_start.lo    ;DS:DI --> first word
        shr     si,cl                   ;Compute inner byte count
        jnz     cmp_word_interval_10   ;loop count + 1 > 0, check it out


;/*
;** Only one word will be affected.  Combine the first and
;** last word masks, and set the loop count to 0.
;*/

        and     ax,bx
        xor     bx,bx
        inc     si                      ;Fall through to set to 0

cmp_word_interval_10:
        dec     si                      ;Dec innerloop count (might become 0)


;/*
;** Words fetched from memory come out with the high order and low
;** order bytes swapped.  This will be handled by swapping the two
;** halves of each mask.
;*/

        xchg    al,ah
        xchg    bl,bh


;/*
;** If all pixels in the first word are altered, combine the
;** first word into the inner loop and clear the first word
;** mask.  Ditto for the last word
;*/

        cmp     ax,dx                   ;Set 'C' if not all pixels 1
        sbb     si,dx                   ;If no 'C', sub -1 (add 1), else sub 0
        cmp     ax,dx                   ;Set 'C' if not all pixels 1
        sbb     ax,dx                   ;If no 'C', sub -1 (add 1), else sub 0

        cmp     bx,dx                   ;Set 'C' if not all pixels 1
        sbb     si,dx                   ;If no 'C', sub -1 (add 1), else sub 0
        cmp     bx,dx                   ;Set 'C' if not all pixels 1
        sbb     bx,dx                   ;If no 'C', sub -1 (add 1), else sub 0

;/*
;** Save the first and last byte masks and the loop count.  If the
;** background mix mode is LeaveAlone, AND the masks with the transparency
;** mask now to save a little time in the loop for multiple passes.  If
;** the background mode is OverPaint, then we need the original clip masks
;** as well as the transparency mask, in case the clipped bytes contain
;** only background pixels to output.
;**
;** If the first byte/word mask is not zero before ANDing in the
;** transparency mask, and becomes zero after ANDing in the transparency
;** mask, then the starting address needs to be updated by the size
;** (byte/word)
;*/

        mov     loc_inner_loop_count,si ;Save the inner loop count
        dec     cx                                ;Map 3==>1, 4==>2 for increment size
        dec     cx
        neg     dx                                ;Set DX = 0001 (was FFFF)
        cmp     ax,dx                             ;'C' only if mask is all zeros
        sbb     dl,dl                             ;DX = 00FF if it was all zeros
        not     dl                                ;DX = 0000 if it was all zeros
        and     dx,cx                             ;Now place size or 0 into DX
        mov     loc_first_mask,ax
        mov     loc_last_mask,bx


;/*
;** If we just created a first byte/word mask of all zero's, then
;** we have to increment the starting address appropriately.
;*/


        cmp     ax,1                    ;Set 'C' if mask is zeros
        sbb     ax,ax                   ;AX = FF if mask is zeros
        and     ax,dx                   ;Only leave increment if mask is zeros
        add     di,ax                   ;Offset start if needed
        clc
        retf

cEnd    <nogen>
        page

;/*
;** ---------------------------------------------------------------;
;**  Raster Operation Subroutines
;** 
;** 
;**  The following subroutines implement the foreground mix modes.
;**  They all have the same register interface.
;** 
;**  The code has been compressed so that the address table could
;**  be stored as bytes instead of words.  This is accomplished
;**  mainly by combining rops with their inverses. The inverse of
;**  rop A is rop B if
;** 
;**        rop A  maps  0 --> x, 1 --> y
;**  whereas
;**        rop B  maps  0 --> y, 1 --> x.
;** 
;**  The pattern will have been pre-inverted for all the "inverses"
;**  (look for the ALT_NEG_PATTERN flag in another_tbl to see which
;**  are the "inverses").
;** 
;**  In addition, some rops were embedded in others, code-wise, in
;**  such a way that the larger could be written to fall through to
;**  the smaller (e.g. transp_dpna and transp_dpa falling through to
;**  transp_ddx).  Unfortunately this makes the code harder to read.
;** 
;**  Many of the rops require a third work register, so BX is used
;**  and reloaded afterwards.
;** 
;**  Entry:
;**        AX = transparency mask
;**        BX = pattern
;**        CX = loop count
;**        DS:DI --> destination
;**        ES:DI --> destination
;**        direction flag cleared
;**  Returns:
;**        CX = 0
;**        DS:DI --> next destination
;**        ES:DI --> next destination
;**        direction flag cleared
;**  Registers Preserved:
;**        BX,SI,BP,DS,ES
;**  Registers Destroyed:
;**        AX,DX,flags
;** 
;** ---------------------------------------------------------------;
;** 
;**  The following table describes the boolean logic necessary for
;**  creating any desired masks from the transparency mask and pattern.
;**  To use it, determine what mask behavior you want (remember to invert
;**  AND masks), look it up, then look across to see how to set it up.
;**  The columns indicate the mask bits for foreground 1's, foreground 0's,
;**  background 1's, and background 0's, respectively.
;** 
;** 
;**                                     s s s s
;**                                    1 0 1 0
;** 
;**                                  g g k k
;**                                 f f b b
;** 
;**         transparency mask (AX)  1 1 0 0
;**                   pattern (BX)  1 0 1 0
;**                                 -------
;**                                 0 0 0 0         0
;**                                 0 0 0 1         not (AX or BX)
;**                                 0 0 1 0         not AX and BX
;**                                 0 0 1 1         not AX
;**                                 0 1 0 0         AX and not BX
;**                                 0 1 0 1         not BX
;**                                 0 1 1 0         AX xor BX
;**                                 0 1 1 1         not (AX and BX)
;**                                 1 0 0 0         AX and BX
;**                                 1 0 0 1         not (AX xor BX)
;**                                 1 0 1 0         BX
;**                                 1 0 1 1         not AX or BX
;**                                 1 1 0 0         AX
;**                                 1 1 0 1         AX or not BX
;**                                 1 1 1 0         AX or BX
;**                                 1 1 1 1         1
;** 
;** 
;**  For example, take the rop DPon with opaque background.  On a plane
;**  by plane basis, the rop's effect is to write 0's where the pattern
;**  is 1, and invert the destination where the pattern is 0.  This can
;**  be done with an AND mask and an XOR mask.  The AND mask clears out
;**  all the bits corresponding to foreground 1's or to background bits.
;**  The XOR mask flips bits corresponding to foreground 0's or
;**  background 1's. The AND mask must be applied first to get the
;**  correct background color.
;** 
;**  DPon: inverse (source or destination), mix mode 10
;** 
;**      pattern
;**     foreground  effective
;**        bit         rop
;**      -------------------
;**         0   -->   ~dest
;**         1   -->     0
;** 
;**         background mix = OverPaint (P):
;** 
;**         AND mask behavior: 0 1 0 0  ==> mask logic: (AX and not BX)
;** 
;**                            | | | |
;**                            | | | +----- clear background 0's
;**                            | | |
;**                            | | +------- clear background 1's
;**                            | |
;**                            | +--------- don't touch foreground 0's
;**                            |
;**                            +----------- clear foreground 1's
;** 
;** 
;**         XOR mask behavior: 0 1 1 0  ==> mask logic: (AX xor BX)
;** 
;**                            | | | |
;**                            | | | +----- don't touch background 0's
;**                            | | |
;**                            | | +------- invert (i.e. set) background 1's
;**                            | |
;**                            | +--------- invert foreground 0's
;**                            |
;**                            +----------- don't touch foreground 1's
;** 
;** 
;** ---------------------------------------------------------------;
;**  The following table describes the behavior of each raster operation
;**  plane by plane, bit by bit. The mapping is from pattern bits to
;**  effective rops (DDx, Dn, D, DDxn) on the destination.  This table
;**  is a convenient reference for figuring out the necessary masks for
;**  rop subroutines, as well as a good display of the symmetry of rop
;**  behavior.
;** 
;**        DDx             PDna            DPa             P
;**          0 ->  0         0 ->  0         0 ->  0         0 ->  0
;**          1 ->  0         1 -> ~dest      1 ->  dest      1 ->  1
;** 
;**        DPon            Dn              DPxn            PDno
;**          0 -> ~dest      0 -> ~dest      0 -> ~dest      0 -> ~dest
;**          1 ->  0         1 -> ~dest      1 ->  dest      1 ->  1
;** 
;**        DPna            DPx             D               DPo
;**          0 ->  dest      0 ->  dest      0 ->  dest      0 ->  dest
;**          1 ->  0         1 -> ~dest      1 ->  dest      1 ->  1
;** 
;**        Pn              DPan            DPno            DDxn
;**          0 ->  1         0 ->  1         0 ->  1         0 ->  1
;**          1 ->  0         1 -> ~dest      1 ->  dest      1 ->  1
;** 
;** 
;** ---------------------------------------------------------------;
;**  Catalog of masks for raster operations:
;** 
;**        If the inverse is mentioned at the right, then the
;**        rop has been implemented by pre-inverting the pattern
;**        and executing the inverse rop's subroutine.
;** 
;**        DDx
;**            D
;**                AND:    not AX
;**            P
;**                AND:    not AX and BX
;** 
;**        DPon                                    inverse of PDna
;**            D
;**                AND:    not (AX and BX)
;**                XOR:    AX and not BX
;**            P
;**                AND:    AX and not BX
;**                XOR:    AX xor BX
;** 
;**        DPna                                    inverse of DPa
;**            D
;**                AND:    not (AX and BX)
;**            P
;**                AND:    AX and not BX
;**                XOR:    not AX and BX
;** 
;**        Pn                                      inverse of P
;**            D   no masks (uses double-xor trick)
;**            P   no masks (uses rep stosw)
;** 
;**        PDna
;**            D
;**                AND:    not AX or BX
;**                XOR:    AX and BX
;**            P
;**                AND:    AX and BX
;**                XOR:    BX
;** 
;**        Dn
;**            D
;**                XOR:    AX
;**            P
;**                AND:    AX
;**                XOR:    AX or BX
;** 
;**        DPx
;**            D
;**                XOR:    AX and BX
;**            P
;**                AND:    AX
;**                XOR:    BX
;** 
;**        DPan
;**            D
;**                OR:     AX and not BX
;**                XOR:    AX and BX
;**            P
;**                OR:     not (AX and BX)
;**                XOR:    not (AX xor BX)
;** 
;**        DPa
;**            D
;**                AND:    not AX or BX
;**            P
;**                AND:    AX and BX
;**                XOR:    not AX and BX
;** 
;**        DPxn                                    inverse of DPx
;**            D
;**                XOR:    AX and not BX
;**            P
;**                AND:    AX
;**                XOR:    AX xor BX
;** 
;**        D
;**            D   no masks (just advance destination pointer)
;**            P
;**                AND:    AX
;**                XOR:    not AX and BX
;** 
;**        DPno                                    inverse of DPo
;**            D
;**                OR:     AX and not BX
;**            P
;**                AND:    AX and BX
;**                XOR:    AX xor BX
;** 
;**        P
;**            D   no masks (uses double-xor trick)
;**            P   no masks (uses rep stosw)
;** 
;** 
;**        PDno                                    inverse of DPan
;**            D
;**                AND:    not (AX and BX)
;**                XOR:    AX
;**            P
;**                AND:    AX and not BX
;**                XOR:    AX or BX
;** 
;**        DPo
;**            D
;**                OR:     AX and BX
;**            P
;**                AND:    AX and not BX
;**                XOR:    BX
;** 
;**        DDxn
;**            D
;**                OR:     AX
;**            P
;**                OR:     AX or BX
;** 
;** ---------------------------------------------------------------;
;*/

        assumes ds,nothing
        assumes es,nothing

rops_start      label   byte

transp_dpna     proc    near
transp_dpa      proc    near
        not     ax
        or      ax,bx
        not     ax                      ;compensate for "not ax" in transp_ddx
        errn$   transp_ddx
transp_dpa      endp
transp_dpna     endp

transp_ddx      proc    near
        not     ax
@@:
        and     [di],ax
        inc     di
        inc     di
        loop    @B
        ret
transp_ddx      endp

transp_pn       proc    near
transp_p        proc    near
        not     ax                      ;Need inverse for unaltered bits
        xchg    ax,dx                   ;Need to use the work register
@@:
        mov     ax,[di]                 ;Quicker to dest into a register
        xor     ax,bx                   ;Invert bits which will not change
        and     ax,dx                   ;Set new bits to 0
        xor     ax,bx                   ;Invert unchanged again, set new bits
        stosw                           ;Store and update destination ptr
        loop    @B                      ;Until all have been processed
        ret
transp_p        endp
transp_pn       endp

transp_dpon     proc    near
transp_pdna     proc    near
        push    bx
        mov     dx,bx                   ;Need to use the work register
        and     bx,ax                   ;XOR mask
        not     ax
        or      dx,ax                   ;AND mask
@@:
        mov     ax,[di]                 ;Quicker to have dest into a register
        xor     ax,bx                   ;Invert bits as needed
        and     ax,dx                   ;Set bits to 0 as needed
        stosw                           ;Store and update destination ptr
        loop    @B                      ;Until all have been processed
        pop     bx
        ret
transp_pdna     endp
transp_dpon     endp

transp_dpxn     proc    near
transp_dpx      proc    near
        and     ax,bx
        errn$   transp_dn
transp_dpx      endp
transp_dpxn     endp

transp_dn       proc    near
@@:
        xor     [di],ax
        inc     di
        inc     di
        loop    @B
        ret
transp_dn       endp

transp_pdno     proc    near
transp_dpan     proc    near
        push    bx
        mov     dx,bx                   ;Need to use the work register
        and     bx,ax                   ;XOR mask
        not     dx                      ;Map 0 ==> 1 for ORing
        and     dx,ax                   ;OR mask

@@:
        mov     ax,[di]                 ;Quicker to have dest into a register
        xor     ax,bx                   ;Invert bits as needed
        or      ax,dx                   ;Set bits to 1 as needed
        stosw                           ;Store and update destination ptr
        loop    @B                      ;Until all have been processed
        pop     bx
        ret
transp_dpan     endp
transp_pdno     endp

transp_dpno     proc    near
transp_dpo      proc    near
        and     ax,bx
        errn$   transp_ddxn
transp_dpo      endp
transp_dpno     endp

transp_ddxn     proc    near
@@:
        or      [di],ax
        inc     di
        inc     di
        loop    @B
        ret
transp_ddxn     endp

;/*
;** transp_d
;**
;** This is only used when the foreground is D, the background
;** is P, and there are clipped words to output.  The pointer
;** must be updated so the inner loop code will fill in the
;** background bits correctly.
;*/

transp_d        proc    near
        add     cx,cx
        add     di,cx                   ;just advance the pointer
        xor     cx,cx
        ret
transp_d        endp


;/***************************************************************************
;*
;* FUNCTION NAME = ScanlineMonoDeinit
;*
;* DESCRIPTION   = Outgoing chores for polyscanlines to a monochrome bitmap 
;*
;*                 Registers Preserved:       
;*                       NONE
;*                 Registers Destroyed:       
;*                       ALL
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;*                 NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineMonoDeinit,FAR
cBegin  <nogen>
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = ScanlineMonoDeinit
;*
;* DESCRIPTION   = Initialize for polyscanlines on a color bitmap. 
;*
;*                 Registers Preserved:       
;*                       NONE
;*                 Registers Destroyed:       
;*                       ALL
;*
;* INPUT         = Stack frame as per PolyScanLine. 
                   DS:SI   =       -> ddc 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;*                 NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

;/*
;**  Define the fragments here for the compiled polyscanline code          
;*/

MAJ1MIN1_BEGINFRAG      =       $                                       
        mov     al,bl           ; fetch bg color                        
        rol     dl,1            ; rotate mask, next pixel bg pixel ?    
        jnc     $               ; yes...                                
tmpoff                  =       $ - 1                                   
        mov     al,bh           ; no -- fetch fg color                  
MAJ1MIN1_BEGINFRAG_LEN  =       $ - MAJ1MIN1_BEGINFRAG                  
MAJ1MIN1_JNC_FIXUP      =       tmpoff - MAJ1MIN1_BEGINFRAG             
MAJ1MIN1_PAT_FIXUP      =       0                                       
                                                                        
MAJ1MIN2_BEGINFRAG      =       $                                       
        mov     al,[7fffh][si]  ; fetch brush pixel                     
tmpoff1                 =       $ - 2                                   
        rol     dl,1            ; rotate mask, next pixel bg pixel ?    
        jnc     $               ; yes...                                
tmpoff                  =       $ - 1                                   
MAJ1MIN2_BEGINFRAG_LEN  =       $ - MAJ1MIN2_BEGINFRAG                  
MAJ1MIN2_JNC_FIXUP      =       tmpoff - MAJ1MIN2_BEGINFRAG             
MAJ1MIN2_PAT_FIXUP      =       -($ - tmpoff1)                          
                                                                        
MAJ1MIN1_ENDFRAG        =       $                                       
        stosb                   ; store result to destination           
        loop    $               ; for all pixels of the scan...         
tmpoff                  =       $ - 1                                   
        retf                                                            
MAJ1MIN1_ENDFRAG_LEN    =       $ - MAJ1MIN1_ENDFRAG                    
MAJ1MIN1_JNC_TARGET     =       0                                       
LOOP_FIXUP              =       -($ - tmpoff)                           
                                                                        
MAJ1MIN2_ENDFRAG        =       $                                       
        stosb                   ; store result to destination           
        inc     si              ; increment brush index                 
        test    si,7            ; ovf to brush scan begin ?             
        jnz     @F              ; no...                                 
        sub     si,8            ; yes -- rewind to scan begin           
@@:     loop    $               ; for all pixels of the scan...         
        retf                                                            
MAJ1MIN2_ENDFRAG_LEN    =       $ - MAJ1MIN2_ENDFRAG                    
MAJ1MIN2_JNC_TARGET     =       0                                       
                                                                        
;/*
;** determine sum of maximum size begin and end fragment lengths
;*/
                                                                        
MaxInstanceLen          =       0                                       
        if      (MAJ1MIN1_BEGINFRAG_LEN GT MaxInstanceLen)              
MaxInstanceLen          =       MAJ1MIN1_BEGINFRAG_LEN                  
        endif                                                           
        if      (MAJ1MIN2_BEGINFRAG_LEN GT MaxInstanceLen)              
MaxInstanceLen          =       MAJ1MIN2_BEGINFRAG_LEN                  
        endif                                                           
tmplen                  =       0                                       
        if      (MAJ1MIN1_ENDFRAG_LEN GT MaxInstanceLen)                
tmplen                  =       MAJ1MIN1_ENDFRAG_LEN                    
        endif                                                           
        if      (MAJ1MIN2_ENDFRAG_LEN GT MaxInstanceLen)                
tmplen                  =       MAJ1MIN2_ENDFRAG_LEN                    
        endif                                                           
MaxInstanceLen          =       MaxInstanceLen + tmplen                 
                                                                        
;/*
;** Tables used to build the compiled code
;*/
                                                                        
FragTable       struc                                                   
ft_BeginFrag    dw      ?               ; offset to begin frag          
ft_BeginLen     dw      ?               ; length of begin frag          
ft_JncFixup     dw      ?               ; offset to jnc fixup           
ft_PatFixup     dw      ?               ; offset to pat fixup           
ft_EndFrag      dw      ?               ; offset to end frag            
ft_EndLen       dw      ?               ; length of end frag            
ft_JncTarget    dw      ?               ; offset to jnc target          
ft_dummy        dw      ?               ; placeholder                   
FragTable       ends                                                    
                                                                        
MajMinFrags     equ     this word                                       
                                                                        
        dw      MAJ1MIN1_BEGINFRAG      ; offset to begin frag          
        dw      MAJ1MIN1_BEGINFRAG_LEN  ; length of fragment            
        dw      MAJ1MIN1_JNC_FIXUP      ; offset from start to jnc off  
        dw      MAJ1MIN1_PAT_FIXUP      ; neg. offset to pat offset     
        dw      MAJ1MIN1_ENDFRAG        ; offset to end frag            
        dw      MAJ1MIN1_ENDFRAG_LEN    ; length of end fragment        
        dw      MAJ1MIN1_JNC_TARGET     ; offset from start to jnc targ.
        dw      0                       ; dummy                         
                                                                        
        dw      MAJ1MIN2_BEGINFRAG      ; offset to begin frag          
        dw      MAJ1MIN2_BEGINFRAG_LEN  ; length of fragment            
        dw      MAJ1MIN2_JNC_FIXUP      ; offset from start to jnc off  
        dw      MAJ1MIN2_PAT_FIXUP      ; neg. offset to pat offset     
        dw      MAJ1MIN2_ENDFRAG        ; offset to end frag            
        dw      MAJ1MIN2_ENDFRAG_LEN    ; length of end fragment        
        dw      MAJ1MIN2_JNC_TARGET     ; offset from start to jnc targ.
        dw      0                       ; dummy                         
                                                                        
;/*
;** The fragments for the foreground/background logical operations.
;*/
                                                                        
LogicalOpFrags  equ     this byte                                       
FRAG_DDX        =       $                                               
        sub     al,al                                                   
FRAG_DPON       =       $                                               
        or      al,es:[di]                                              
        not     al                                                      
FRAG_DPNA       =       $                                               
        not     al                                                      
        and     al,es:[di]                                              
FRAG_PN         =       $                                               
        not     al                                                      
FRAG_PDNA       =       $                                               
        mov     ah,es:[di]                                              
        not     ah                                                      
        and     al,ah                                                   
FRAG_DN         =       $                                               
        mov     al,es:[di]                                              
        not     al                                                      
FRAG_DPX        =       $                                               
        xor     al,es:[di]                                              
FRAG_DPAN       =       $                                               
        and     al,es:[di]                                              
        not     al                                                      
FRAG_DPA        =       $                                               
        and     al,es:[di]                                              
FRAG_DPXN       =       $                                               
        xor     al,es:[di]                                              
        not     al                                                      
FRAG_D          =       $                                               
        mov     al,es:[di]                                              
FRAG_DPNO       =       $                                               
        not     al                                                      
        or      al,es:[di]                                              
FRAG_P          =       $                                               
FRAG_PDNO       =       $                                               
        mov     ah,es:[di]                                              
        not     ah                                                      
        or      al,ah                                                   
FRAG_DPO        =       $                                               
        or      al,es:[di]                                              
FRAG_DDXN       =       $                                               
        mov     al,0ffh                                                 
FRAG_NO_MORE    =       $                                               
                                                                        
;/*
;** The table indexing into the logical operation fragments.
;*/
                                                                        
tmpoff  =       0                       ; this must start off 0         
tmplen  =       0                       ; this too                      
                                                                        
MakeFragEntry   macro   fragname                                        
tmpoff1 =       fragname-FRAG_DDX                                       
        db      tmpoff1                                                 
        if      ((tmpoff1 - tmpoff) GT tmplen)                          
tmplen  =       (tmpoff1 - tmpoff)                                      
        endif                                                           
tmpoff  =       tmpoff1                                                 
                endm                                                    
                                                                        
LogicalOpFragTable      equ     this word                               
        MakeFragEntry   FRAG_DDX                                        
        MakeFragEntry   FRAG_DPON                                       
        MakeFragEntry   FRAG_DPNA                                       
        MakeFragEntry   FRAG_PN                                         
        MakeFragEntry   FRAG_PDNA                                       
        MakeFragEntry   FRAG_DN                                         
        MakeFragEntry   FRAG_DPX                                        
        MakeFragEntry   FRAG_DPAN                                       
        MakeFragEntry   FRAG_DPA                                        
        MakeFragEntry   FRAG_DPXN                                       
        MakeFragEntry   FRAG_D                                          
        MakeFragEntry   FRAG_DPNO                                       
        MakeFragEntry   FRAG_P                                          
        MakeFragEntry   FRAG_PDNO                                       
        MakeFragEntry   FRAG_DPO                                        
        MakeFragEntry   FRAG_DDXN                                       
        MakeFragEntry   FRAG_NO_MORE                                    
                                                                        
MaxInstanceLen  =       MaxInstanceLen + tmplen                         
                                                                        

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineColorInit,FAR
cBegin  <nogen>

;/*
;** Save local copy of y origin of pattern. This will be added to the y of
;** the scanline currently being output, and then mod'ed with 7 to get the
;** particular scan of the pattern to be used.
;*/

        mov     ax,loc_bm_height                  ;Correct for the fact we align top left
        neg     ax                                ;  instead of bottom left
        add     al,byte ptr [si].ddc_pa.pa_ptsOrg.pts_y
        mov     loc_pat_row,ax          ; get copy of y coord of brush origin

;/*
;** Latch the states of several parameters needed for special case detection
;** during scanline output. Several circumstances produce situations which
;** are more quickly executed by special case code:
;**
;**       do nothing
;**       ----------
;** CASE 1        - transparency mask is all 0s and bg mix is LeaveAlone
;** CASE 2        - transparency mask is all 1s and fg mix is LeaveAlone
;**
;**       use rep stos
;**       ------------
;** CASE 3        - transparency mask is all 1s, brush is solid, fg mix is ROP_P
;** CASE 4        - transparency mask is all 0s, brush is solid, bg mix is ROP_P
;** CASE 5        - the fg mix and the bg mix is ROP_P, the brush is solid,
;**                 and the fg and bg colors are equal.
;*/

        sub     al,al                   ; initial acceleration flags

        mov     ah,[si].ddc_pa.pa_ba.ba_bmix; fetch the fg mix
        cmp     ah,ROP_D                ; is the fg mix LeaveAlone ?
        jne     @F                      ; no...
        or      al,FG_IS_LEAVEALONE     ; yes -- set flag
@@:     cmp     ah,ROP_P                ; is the fg mix Replace ?
        je      @F                      ; yes...
        or      al,FG_IS_NOT_REPLACE    ; no -- set flag
@@:

        mov     dl,[si].ddc_pa.pa_ba.ba_bkmix
        or      al,BG_IS_LEAVEALONE     ; Assume Leavealone
        cmp     dl,ROP_D                ; is the bg mix LeaveAlone ?
        je      @F                      ; yes...
        and     al,NOT BG_IS_LEAVEALONE
        cmp     dl,ROP_P                ; is the bg mix Replace ?
        jne     @F
        cmp     ah,ROP_P                ; is fg mix also replace ?
        je      @F                      ; yes...
        or      al,FG_MIX_IS_NOT_BG_MIX ; no -- set flag
@@:

        test    [si].ddc_pa.pa_fb,PA_SINGLE_CLR; is the brush solid ?
        jnz     @F                      ; yes...
        or      al,BRUSH_IS_PATTERNED   ; no -- set flag
@@:

        mov     dl,[si].ddc_pa.pa_ba.ba_ipcBack.ipc_bClr; fetch bg color
        mov     dh,[si].ddc_pa.pa_ba.ba_ipc.ipc_bClr; fetch fg color
        mov     loc_both_colors,dx      ; save fg,bg colors
        cmp     dl,dh                   ; are they the same ?
        je      @F                      ; yes...
        or      al,FG_COLOR_IS_NOT_BG_COLOR; no -- set flag
@@:

        mov     loc_brush_accel,al      ; save brush acceleration flags

;/*
;** Check now for the rep stos case which can be detected before doing any
;** scanlines. If we have this case, then we don't have to compile any code
;** at all!
;*/

        test    al,REP_STOS_IS_OK       ; is rep stos ok ?
        jnz     @F                      ; no -- must compile...
        jmp     sci_exit                ; yes -- then all done...
@@:

;/*
;** We will use the CS-aliased procedure Compile Code segment used by the compiled
;** blt to compile our code into. We don't use it in the same way as blt does
;** since we are not running our stack segment within it. But it is safe for
;** us to build code into it and call it later on through its CS-alias.
;*/

        mov     es,MyCmplCodeData       ; selector to the instance data area
        assumes es,CompileCodeData
        lea     di,proc_stack_area      ; offset to where we build
ifdef   FIREWALLS
                                        ; get -> end of instance data buffer
        lea     dx,proc_initial_sp
        sub     dx,di                   ; get its size
        cmp     dx,MaxInstanceLen       ; can we fit our code in the buffer ?
        jae     @F                      ; yes...
        rip     text,<ScanlineColorInit -- CompileCodeData buffer too small for compiled code>
@@:
endif
        mov     loc_compiled_proc.off,di; save the address for later
        mov     dx,selCompileCode
        mov     loc_compiled_proc.sel,dx; save the selector address for later
        missing_code    <Do we have to grab the proc_sem semaphore here ?>

;/*
;**  Now the semi-hard part -- we will build a compiled polyscanline routine
;**  into the instance data area. There are two major cases of compiled routine.
;**  Code generated for each case will look like the following:
;** 
;**        Case 1: solid brush
;**        -------------------
;** 
;**          $0:   mov     al,bl           ; fetch bk color
;**                rol     dl,1            ; rotate mask, next pixel bg pixel ?
;**                jnc     sb_bk           ; yes...
;**                mov     al,bh           ; no -- fetch fg color
;**                <fg logic operation>    ; do fg logical operation
;**                jmp     short @F
;**         sb_bk: <bk logic operation>
;**            @@: stosb                   ; store result to destination
;**                loop    $0              ; for all pixels of the scan...
;**                ret
;** 
;**        Case 2: patterned brush
;**        -----------------------
;** 
;**            $0: mov     al,ds:brushOFFSET[si]; no -- fetch brush fg pixel
;**                rol     dl,1            ; rotate mask, next pixel bg pixel ?
;**                jnc     pb_bk           ; yes...
;**                <fg logic operation>    ; do fg logical operation
;**                jmp     short @F
;**         pb_bk: <bk logic operation>
;**            @@: stosb                   ; store result to destination
;**                inc     si              ; increment brush index
;**                test    si,7            ; ovf to brush scan begin ?
;**                jnz     @F              ; no...
;**                sub     si,8            ; yes -- rewind to scan begin
;**            @@: loop    $0              ; for all pixels of the scan...
;**                ret
;*/

;/*
;** Determine index into compiled code fragments table. AL at this point has
;** a copy of the brush accelerator flags which has all the info we need to
;** determine the index into the table.
;*/

        mov     bl,al                   ; accelerator flags into index reg
        sub     bh,bh                   ; zero-extend
        and     bl,BRUSH_IS_PATTERNED   ; isolate 2 needed bits
        .errnz  BRUSH_IS_PATTERNED - 00010000b
        add     bx,CodeOFFSET MajMinFrags; BX is offset to fragment structure

;/*
;** Copy the begin fragment of the compiled polyscanline code into the instance
;** data area.
;*/

        mov     dx,di                   ; remember starting offset
        cld                             ; work up in address

        mov     si,cs:[bx].ft_BeginFrag ; load offset to begin fragment
        mov     cx,cs:[bx].ft_BeginLen  ; length of fragment
        rep     movs es:byte ptr[di],cs:byte ptr[si]; move fragment

;/*
;** If the brush is not solid then we must stuff the offset of the brush into
;** the pattern fetch instruction.
;*/

        mov     cx,cs:[bx].ft_PatFixup  ; 0 if brush is solid
        jcxz    @F                      ; the brush is solid...

        mov     si,arg_lp_ddc.off       ; offset to ddc begin
        add     si,ddc_pa.pa_abColor    ; add in offset to color brush in ddc
        xchg    bx,cx
        mov     es:[di][bx],si          ; stuff brush offset
        xchg    bx,cx
@@:

;/*
;** Copy the code fragment for the foreground logical operation into the instance
;** data area.
;*/

ifdef   FIREWALLS
        mov     si,arg_lp_ddc.off       ; get offset to ddc
        ddc?    si                      ; make sure DS:SI points to ddc
        assert  ah,E,[si].ddc_pa.pa_ba.ba_bmix
endif
        mov     al,ah                   ; fg rop was still in AH
        cbw                             ; zero-extend it
        mov     si,ax
        mov     ax,LogicalOpFragTable[si]; fetch offset to fragment
        mov     cl,ah                   ; fetch offset to next fragment
        cbw                             ; zero-extend offset to fragment
        mov     si,ax
        add     si,CodeOFFSET LogicalOpFrags
        sub     cl,al                   ; get # bytes in fragment
        sub     ch,ch
        rep     movs es:byte ptr[di],cs:byte ptr[si]

;/*
;** Add code for JMP SHORT instruction between fg and bk fragment
;** And remember the location of the beginning of bk fragment (for JMP offset)
;*/

JMP_SHORT       equ     000EBh          ; we are going to insert a jump

        mov     ax,JMP_SHORT            ; Jmp short with 1 byte offset
        stosw
        push    di                      ; save this pointer to the offset

;/*
;** At this point we are able to provide the short jump offset for the JNC
;** instruction in the beginning fragment.
;*/

        mov     ax,di                   ; current offset into instance data
        sub     ax,dx                   ; # bytes so far in data area
        add     ax,cs:[bx].ft_JncTarget ; # bytes into end frag to target
        mov     cx,cs:[bx].ft_JncFixup  ; fetch JNC instruction field offset
        sub     ax,cx                   ; negative bias by...
        dec     ax                      ;    (offset to jnc offset field + 1)
                                        ; AX now has short jump offset for JNC
        mov     si,dx                   ; make ES:SI point to JNC offset
        add     si,cx
        mov     es:[si],al              ; stuff JNC short jump offset

;/*
;** Copy the code fragment for the background logical operation into the instance
;** data area.
;*/

        mov     al,byte ptr loc_bk_raster_op ; get background mixmode

ifdef   FIREWALLS
        mov     si,arg_lp_ddc.off       ; get offset to ddc
        ddc?    si                      ; make sure DS:SI points to ddc
        assert  al,E,[si].ddc_pa.pa_ba.ba_bkmix
endif

        cbw                             ; zero-extend it
        mov     si,ax
        mov     ax,LogicalOpFragTable[si]; fetch offset to fragment
        mov     cl,ah                   ; fetch offset to next fragment
        cbw                             ; zero-extend offset to fragment
        mov     si,ax
        add     si,CodeOFFSET LogicalOpFrags
        sub     cl,al                   ; get # bytes in fragment
        sub     ch,ch
        rep     movs es:byte ptr[di],cs:byte ptr[si]

;/*
;** At this point we are able to provide the short jump offset for the JMP SHORT
;** instruction at the end of fg fragment.
;*/

        pop     ax                      ; Starting address of bk fragment
        mov     si,ax
        dec     si                      ; make ES:SI point to JMP offset
        neg     ax
        add     ax,di                   ; AX now has short jump offset for JMP
        mov     es:[si],al              ; stuff JMP short jump offset

;/*
;** Copy the end fragment of the compiled polyscanline code into the instance
;** data area.
;*/

        mov     si,cs:[bx].ft_EndFrag   ; load offset to begin fragment
        mov     cx,cs:[bx].ft_EndLen    ; length of fragment
        rep     movs es:byte ptr[di],cs:byte ptr[si]; move fragment


;/*
;** Fixup of the short jump field of the LOOP instruction in the end fragment.
;*/

        sub     dx,di                   ; negative # bytes in data buffer
        sub     dl,(LOOP_FIXUP+1)       ; adjust for bytes between loop and end
        mov     es:[di][LOOP_FIXUP],dl  ; stuff offset to top of loop

sci_exit:
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = scd_xyaddr
;*
;* DESCRIPTION   = Calculate a far pointer into the dest bitmap located in    
;*                 local memory.                                              
;*
;*                 Registers Preserved:       
;*                       DX,BP
;*                 Registers Destroyed:       
;*                       AX,BX,CX,SI,DI,DS,ES,FLAGS
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = CX      =       # pixels to fill on scan                 
;*                 [ES:DI] =       -> byte containing 1st pixel of interest 
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    scd_xyaddr,FAR
cBegin  <nogen>

        lds     bx,arg_lp_ddc           ; need -> to ddc
        assumes ds,nothing
        ddc?    bx
        mov     bx,[bx].ddc_npsd        ; get the surface definition from ddc

        mov     ax,loc_saved_y          ; src y origin
        mov     di,[bx].sd_pBits.sel    ; bitmap selector

;/*
;** Test for huge bitmap. If we have a huge bitmap then we need to compute
;** the starting segment, and then do the xyaddr into that segment.
;*/

        test    [bx].sd_fb,SD_HUGE      ; is this a huge bitmap
        jz      scd_xyaddr_small_bitmap ; no...

        mov     si,DOSHUGEINCR          ; yes -- load sel incr to next sel
        mov     cx,[bx].sd_cySeg        ; # scans / segment

@@:     add     di,si                   ; assume advance to next segment
        sub     ax,cx                   ; advance to next segment
        jae     @B                      ; not in segment yet

        sub     di,si                   ; we overshot into next segment
        add     ax,cx                   ; get scan # in correct segment

scd_xyaddr_small_bitmap:

        mov     es,di
        assumes es,nothing
        mov     cx,dx                   ; preserve DX
        mul     [bx].sd_cbScan          ; width in bytes / scan of dest
        mov     dx,cx                   ; restore DX
        mov     di,[bx].sd_pBits.off    ; -> bits of source bitmap
        add     di,ax                   ; -> start of scan of interest
        mov     ax,loc_points.pts_x     ; get x origin
        add     di,ax                   ; -> starting pixel in scan of interest

        mov     cx,loc_points.pts_y     ; get (right x + 1) on scan
        sub     cx,ax                   ; now CX is # pixels to fill

        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = ScanlineColorDoer
;*
;* DESCRIPTION   = Display polyscanlines on a color bitmap.  
;*
;*                 Registers Preserved:       
;*                       NONE
;*                 Registers Destroyed:       
;*                       ALL
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    ScanlineColorDoer,FAR
cBegin  <nogen>

        mov     dh,loc_brush_accel      ; fetch the accelerator flags

;/*
;** Detect the first special case (CASE 5 described in the init routine).
;*/

        test    dh,REP_STOS_IS_OK       ; can we immediately rep stos ?
        jnz     scd_test_cases          ; no -- wasn't CASE 5...

scd_CASE_4_and_5:

        call    scd_xyaddr              ; calculate -> into destination bitmap
        mov     al,byte ptr loc_both_colors[0]; fetch color to fill with

;/*
;** Fill the scanline in the fastest possible rep stos manner.
;**
;**       AL      =       color to fill with
;**       CX      =       # pixels to fill
;**       ES:DI   =       -> into destination bitmap
;**       direction flag is clear
;*/

scd_rep_stos:

        test    di,1                    ; storing to initial odd byte ?
        jz      @F                      ; no...
        stosb                           ; yes -- store the odd initial pixel
        dec     cx                      ; count must reflect pixel written
@@:     mov     ah,al                   ; replicate color for word stores
        shr     cx,1                    ; get word count, odd byte to 'C'
        rep     stosw                   ; fill words, 'C' preserved
        adc     cx,cx                   ; get possible odd byte count to CX
        rep     stosb                   ; perhaps fill last pixel
        jmp     short scd_exit          ; done...


scd_test_cases:

;/*
;** Fetch the relevant scan of the transparency mask. This is equivalent to
;** phasing in y.
;*/

        mov     di,loc_saved_y          ; current row to display scan on
        add     di,loc_pat_row          ; y org of brush
        and     di,SIZE_PATTERN - 1     ; keep mod pattern size
        mov     dl,loc_trans_mask[di]   ; get current transparency mask row

;/*
;** Test for special CASEs 1-4. These cases never require the transparency
;** mask to be phased in x so we will test them before we would!
;*/

        or      dl,dl                   ; is the transparency mask all 0s ?
        jnz     scd_trans_mask_not_0s   ; no...

        test    dh,BG_IS_LEAVEALONE     ; bg mix doesn't touch destination ?
        jnz     scd_exit                ; right -- CASE 1, bail out now...

        test    dh,BRUSH_IS_PATTERNED   ; is it a solid brush ?
        jz      scd_CASE_4_and_5        ; yes -- CASE 4, can go fast path...

scd_trans_mask_not_0s:

        cmp     dl,0ffh                 ; is the transparency mask all 1s ?
        jne     scd_phase_in_x          ; no -- must go slower path...

        test    dh,FG_IS_LEAVEALONE     ; fg mix doesn't touch destination ?
        jnz     scd_exit                ; yes -- CASE 2, bail out now...

                                        ; is brush solid and fg mix ROP_P?
        test    dh,(BRUSH_IS_PATTERNED+FG_IS_NOT_REPLACE)
        jnz     scd_phase_in_x          ; no -- must go slower path...

        call    scd_xyaddr              ; get -> destination to ES:DI
        mov     al,byte ptr loc_both_colors[1]; fetch fg color to fill with
        jmp     short scd_rep_stos      ; CASE 3, do things the fast way...


;/*
;** Now phase the pattern in x.
;*/

scd_phase_in_x:

        mov     cl,byte ptr loc_points.pts_x; left x of scanline
        and     cl,SIZE_PATTERN - 1     ; keep mod pattern size
        rol     dl,cl                   ; phase pattern to destination

;/*
;** Calculate the pointer of the first pixel to fill in the destination bitmap.
;*/

        call    scd_xyaddr              ; get -> destination to ES:DI

;/*
;** Load up other parameters needed (or perhaps needed) for the compiled
;** scanline fill code and call the code.
;*/

        test    dh,BRUSH_IS_PATTERNED   ; is it a solid brush ?
        jz      scd_brush_is_solid      ; yes...

;/*
;** A non-solid brush must be used -- we need to point to the correct starting
;** row and pixel within that row.
;*/

        mov     si,loc_saved_y          ; current row to display scan on
        add     si,loc_pat_row          ; y org of brush
        and     si,SIZE_PATTERN - 1     ; keep mod pattern size
        shl     si,3                    ; color brush rows are 8 bytes (pixels)
        .errnz  SIZE_PATTERN - 8
        mov     al,byte ptr loc_points.pts_x; left x of scanline
        and     ax,SIZE_PATTERN - 1     ; keep mod pattern size
        add     si,ax                   ; phasing it in x
        mov     ds,arg_lp_ddc.sel       ; point to ddc data segment
        assumes ds,Data
        jmp     short scd_exec_code     ; ready to execute the compiled code...

scd_brush_is_solid:

        mov     bx,loc_both_colors      ; fetch colors

scd_exec_code:

;/*
;**   Registers set up as follows before call to compiled scanline code:
;**  
;**         AX      =       nothing
;**         BL      =       bg color if brush is solid
;**         BH      =       fg color if brush is solid
;**         CX      =       # pixels (bytes) to hit
;**         DL      =       transparency mask
;**         SI      =       offset into color brush if brush is patterned
;**         DS      =       selector of color brush if brush is patterned
;**         ES:DI   =       -> destination bitmap start pixel
;*/

        call    loc_compiled_proc       ; fill the scanline...

scd_exit:
        ret
cEnd

cEnd    <nogen>


;/***************************************************************************
;*
;* FUNCTION NAME = ScanlineColorDeinit
;*
;* DESCRIPTION   = Outgoing chores for polyscanlines on a color bitmap. 
;*
;*                 Registers Preserved:       
;*                       NONE
;*                 Registers Destroyed:       
;*                       ALL
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineColorDeinit,FAR
cBegin  <nogen>
        ret
cEnd    <nogen>
