;*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
;/*****************************************************************************
;*
;* SOURCE FILE NAME = PSCANLN.INC
;*
;* DESCRIPTIVE NAME = Scanline include file
;*
;*
;* VERSION      V2.0
;*
;* DATE         
;*
;* DESCRIPTION  
;*
;* FUNCTIONS    comp_scan_for_bitmap 
;*              get_fill_data   
;*              set_procs_non_ega 
;*              comp_interval   
;*              ega_all_partial 
;*              ega_all_partial_back 
;*              ega_inner_latch  
;*              ega_inner_stosb  
;*              ega_first_last   
;*              do_nothing       
;*              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
;*   03/15/88                     Bob Grudem [bobgru] Partially rewrote to
;*                                properly handle background mix modes.
;*   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).
;*   08/01/88                     Bob Grudem [bobgru] Moved cursor exclusion out.
;*   02/13/89                     Wes Rupel [wesleyr] This will now never be
;*                                called for the device (EGA/VGA), only
;*                                bitmaps.  Much of the work here was
;*                                needless overhead per scanline.
;*   04/17/89                     Wes Rupel [wesleyr] Moved stuff out to
;*                                predigest_data that only needs to be done once.
;*   12/19/89                     Viroon Touranachun [viroont] Cloned from
;*                                ega_all_partial to use with loc_bk_processor
;*                                structure
;*   12/20/89                     Viroon Touranachun [viroont] Modified it to
;*                                handle both foreground and background mixmode
;*                                Viroon Touranachun [viroont] Removed combining
;*                                transparency_mask with first and last_masks.
;*****************************************************************************/


;/*
;** The next two constants define the mask and shift count for different
;** size accesses to the destination surface.  The high byte 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_BYTE_FETCH  equ     0703h                     ;mask for modulus / shift count
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
;**
;** MY_SINGLE_OK    rop can be performed in a single pass
;** NEG_PATTERN     pattern is to be negated before use
;** ALT_NEG_PATTERN same as above (different bit position for convenience)
;** NO_OBJECT       rop doesn't require an object (D, Dn, DDx, DDxn)
;** STOSB_OK        inner loop can use stosb
;*/

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_for_bitmap
;*
;* DESCRIPTION   = 
;*
;*      The starting Y coordinate will be computed.
;*   
;*      The Y of the scan will be saved in case 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
;*
;* INPUT         = NONE
;* OUTPUT        = CL = loop_control flag 
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  comp_scan_for_bitmap

comp_scan_for_bitmap       proc near
        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
@@:
        add     dx,cx                             ;Show in next segment
        sub     ax,bx                             ;See if in this segment
        jnc     @B                                ;Not in current segment, try next
        add     ax,bx                             ;Restore correct Y
        sub     dx,cx                             ;Show correct segment

;/*
;** 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.
;**
;**       Currently:
;**               AX     =  Y coordinate
;**               DX     =  Segment bias for huge bitmaps
;**               DS:SI --> display surface structure
;*/

cs_for_bitmap_40:
        mov     di,[si].sd_cbScan                 ;Get index to next plane
        mov     bx,di                             ;  (and Y multiplier)
        mov     cl,MONO_OP                        ;Assume mono loop
        test    [si].sd_fb,SD_COLOR
        jz      @F
        mov     cl,COLOR_OP                       ;Show color loop
        add     ax,ax                             ;Compute Y to use for offset calculation
        add     ax,ax
        .errnz  BITS_PEL - 4
@@:
        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
;** starting address by this amount.
;*/

        sub     ax,di                             ;Bias offset for looping logic
        mov     loc_scan_start.lo,ax              ;Save address of scan
        mov     loc_next_plane,di                 ;Save index to next plane
        ret

comp_scan_for_bitmap       endp

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_non_ega
;*
;* INPUT         = INDEX_BKGRND flag set to 0 for foreground 
;*                 INDEX_BKGRND flag set to 1 for background
;* OUTPUT        =
;*
;* RETURN-NORMAL = 'C' clear if successful  
;* RETURN-ERROR  = 'C' set if to abort      
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

                public  get_fill_data
get_fill_data   proc near

        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_bClr
        test    loc_some_flags,INDEX_BKGRD
        jz      gfd_brush_acc
        mov     al,[di].ddc_pa.pa_ba.ba_ipcBack.ipc_bClr
        dec     cx

gfd_brush_acc:
        and     al,MM_ALL or MONO_BIT or ONES_OR_ZEROS
        mov     ah,[di].ddc_pa.pa_fb
        and     ah,PA_SINGLE_CLR
        or      ah,al
        .errnz  PA_SINGLE_CLR and (MM_ALL or MONO_BIT or ONES_OR_ZEROS)
        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

;/*
;** All the parameters are already set if it is solid brush. So exit
;*/

gfd_special_cases:
        test    loc_moore_flags,MF_SPECIAL_SOLID
        njnz    gfd_exit                ;No, not a special case

;/*
;** 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
;**                       = BITS_PEL*(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,BITS_PEL * (SIZE cntrl_blk)
        jz      @F
        rip     text,<SCANLINE: Non adjacent processor blocks>
@@:
        pop     ax
endif;FIREWALLS

        and     cx,-(BITS_PEL * (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
;*/
 
        or      loc_some_flags,INDEX_XPARENT     ;Assume any hole is XPARENT
        cmp     al,0FFh
        jne     gfd_check_background_mix
        and     loc_some_flags,NOT INDEX_XPARENT ;But there is no hole
        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
;*/
 
        public  gfd_gather_colors
gfd_gather_colors:



        mov     ch,al                             ;may need mask again soon
        mov     dl,[di].[bx].ddc_pa.pa_abMono
        test    loc_some_flags,INDEX_MONO
        jnz     @F

        mov     al,ah                             ;save brush accelerators
        mov     dl,[di].[bx].ddc_pa.pa_abColor.ipb_abPlane0
        mov     dh,[di].[bx].ddc_pa.pa_abColor.ipb_abPlane1
        mov     cl,[di].[bx].ddc_pa.pa_abColor.ipb_abPlane2
        mov     bl,[di].[bx].ddc_pa.pa_abColor.ipb_abPlane3

@@:                                              ;Test if pattern required
        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     ch                                ;invert transparency mask
        and     bl,ch                             ;isolate background bits in each
        and     cl,ch                             ;  color plane
        and     dl,ch                             ; 
        and     dh,ch                             ; 
        xor     al,al                             ;clear all brush accelerators

;/*
;** Determine whether or not to set the PA_SINGLE_CLR and ONES_OR_ZEROS
;** accelerator bits.  PA_SINGLE_CLR is set if all planes are either
;** 0 or FF.  ONES_OR_ZEROS is set if PA_SINGLE_CLR is set and all planes
;** are identical.
;*/

        mov     ch,0FFh
        cmp     bl,ch
        je      gfd_intense_ok
        or      bl,bl
        jnz     gfd_we_have_colors
gfd_intense_ok:
        cmp     cl,ch
        je      gfd_red_ok
        or      cl,cl
        jnz     gfd_we_have_colors
gfd_red_ok:
        cmp     dl,ch
        je      gfd_green_ok
        or      dl,dl
        jnz     gfd_we_have_colors
gfd_green_ok:
        cmp     dh,ch
        je      gfd_blue_ok
        or      dh,dh
        jnz     gfd_we_have_colors
gfd_blue_ok:
        or      al,PA_SINGLE_CLR

        cmp     bl,cl
        jnz     gfd_we_have_colors
        cmp     bl,dl
        jnz     gfd_we_have_colors
        cmp     bl,dh
        jnz     gfd_we_have_colors
        or      al,ONES_OR_ZEROS

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
;*/
 
        test    loc_sd_fb,SD_DEVICE
        jnz     set_procs_ega                     ; Destination is EGA/VGA

;/*
;** Set up the process addresses for either a color or a mono
;** bitmap.
;**!!! loc_loop_control can be incorrectly set (below) for EGA special cases.
;*/

        public  gfd_acts_like_a_bitmap

gfd_acts_like_a_bitmap:

        mov     si,COLOR_OP
        test    loc_some_flags,INDEX_MONO
        jz      @F
        mov     si,MONO_OP
@@:
        mov     loc_loop_control,si

        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

gfd_got_mask:
        mov     al,80h                            ;D7 = 1 to show nop
        lea     si,loc_processors[MONO_DATA]
        add     si,loc_proc_offset                ;SI-->first control block
        test    loc_some_flags,al
        jnz     set_procs_mono                    ;Handle it for a mono bitmap
        .errnz  INDEX_MONO - 80h                  ;Must be same as AL

set_procs_color:
        mov     ch,bl                             ;Set up for C3 color
        mov     bl,MM_C3
        call    set_procs_non_ega
        mov     ch,cl                             ;Set up for C2 color
        mov     bl,MM_C2
        call    set_procs_non_ega
        mov     ch,dh                             ;Set up for C1 color
        mov     bl,MM_C1
        call    set_procs_non_ega
        mov     bl,MM_C0                          ;EGA map mask value

set_procs_mono:
        mov     ch,dl                             ;Set up for C0 or mono color
        call    set_procs_non_ega
        mov     loc_fetch_data,PSL_WORD_FETCH     ;Set for word alignment
        shl     al,1                              ;Set 'C' if operation can be skipped
gfd_exit:
        ret


;/*
;** The EGA (should be said with a tone of pending doom).
;**
;** If the brush has ONES_OR_ZEROS set in the accelerator,
;** the raster op will be mapped to one of the rops which doesn't
;** require a drawing object (D, Dn, DDx, DDxn).  These Rops can
;** always be special cased.
;**
;** If the foreground raster op, after remapping, is D, return with
;** carry set so that no more time is spent on this scanline.  Note
;** that this only works for solid brushes, where the transparency
;** mask is all 1's (also has PA_SINGLE_CLR set).
;**
;** 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
;**       SI = raster op
;**       DI = DDC
;*/

set_procs_ega:
        or      loc_clean_up,CU_PLANE_SEL
        mov     ch,bl                             ;put C3 color in safe place
        or      al,al                             ;PA_SINGLE_CLR must be set for
        jns     gfd_chk_one_pass_rop              ;  ONES_OR_ZEROS to be valid
        .errnz  PA_SINGLE_CLR - 10000000b
        test    al,ONES_OR_ZEROS                  ;If ONES_OR_ZEROS isn't set, then
        jz      gfd_chk_one_pass_rop              ;  the rop cannot be mapped
        ror     dl,1                              ;Must be all 0's or all 1's
        rcl     si,1                              ;Add color into the rop
        mov     bx,si
        mov     bl,map_1s_0s[bx]                  ;Get new rop
        cmp     bl,ROP_D                          ;If the rop is D
        stc                                       ;  set the carry to say that
        je      gfd_exit                          ;  we're all done
        mov     ah,another_tbl[bx]                ;Need to reload these
        mov     si,bx                             ;save remapped rop

gfd_chk_one_pass_rop:

        mov     loc_fetch_data,PSL_BYTE_FETCH     ;Show byte alignment
        mov     bl,ch                             ;bitmap code expects C3 here
        cmp     loc_transparency_mask,0FFFFh      ;Any background bits visible?
        njne    gfd_acts_like_a_bitmap            ;if yes, treat as a bitmap
        test    ah,MY_SINGLE_OK                   ;Can the ROP occur in one operation?
        jz      set_procs_color_vect              ;  No, treat as a bitmap


;/*
;** The ROP can be performed in one pass on the EGA for solid brushes.
;** If the brush really is solid, then set up the EGA and the looping
;** logic as necessary for a one pass operation.  If the brush isn't
;** solid, both P and Pn may still be able to be accelerated.
;**
;** If there are no transparent pixels, and the raster operation
;** can use STOSB in it's inner loop, then set BX to 0FFFFh to
;** flag that STOSB is ok.  Otherwise, set BX = 0.
;*/

        mov     bx,STOSB_OK shl 8 + INDEX_XPARENT
        xor     bh,ah                             ;Invert STOSB_OK flag
        or      bh,loc_some_flags                 ;OR in INDEX_XPARENT
        and     bh,bl                             ;BH = 0 if we can use STOSB
        cmp     bh,bl                             ;Set 'C' if STOSB can be used
        sbb     bx,bx                             ;FFFF if STOSB ok, 0000 otherwise
        .errnz  INDEX_XPARENT - STOSB_OK          ;Must be the same bits

        or      al,al                             ;Solid brush?
        jns     set_procs_ega_50                  ;  No, but might still handle P and Pn
        .errnz  PA_SINGLE_CLR - 10000000b

        public  scanline_solid_brush
scanline_solid_brush:

;/*
;** The first and last bytes will use ega_first_last.  The inner loop
;** generally will also use ega_first_last, but may be able to use
;** ega_inner_stosb.   It will be able to do this if there are no
;** transparent bits and the STOSB_OK flag is set for the rop.
;*/

        mov     di,loc_proc_offset                  ;DI-->first element
        mov     dx,CodeOFFSET ega_first_last
        mov     loc_processors[MONO_DATA][di].first_last_proc,dx
        and     bx,ega_inner_stosb-ega_first_last
        add     dx,bx                               ;Add 0 or delta to ega_inner_stosb if stosb ok
        mov     loc_processors[MONO_DATA][di].inner_loop_proc,dx

;/*
;** Program the EGA for the correct mode of operation as defined
;** by the raster operation.
;*/

        mov     dx,EGA_BASE + GRAF_ADDR
        and     al,Code_rop_pen_and[si]           ;Set correct color for the ROP
        xor     al,Code_rop_pen_xor[si]
        and     al,MM_ALL                         ;Only leave bits of interest
        mov     ah,al
        mov     al,GRAF_SET_RESET
        out16   dx,ax
        mov     ax,MM_ALL shl 8 + GRAF_ENAB_SR
        out16   dx,ax                             ;Enable all planes for set/reset
        mov     al,GRAF_DATA_ROT                  ;Set Data Rotate reg value
        mov     ah,Code_rop_data_r[si]
        out16   dx,ax


;/*
;** Just about done setting up the EGA for whatever.  Leave
;** the Graphics Controller address register pointing to
;** the Bitmask register, show that the operation must occur,
;** that plane selection isn't needed, and that only one loop
;** is required.
;** Currently:
;**       DI = index to first control block element
;*/

        public  set_procs_ega_40
set_procs_ega_40:
        and     loc_clean_up,not CU_PLANE_SEL
        mov     loc_loop_control,MONO_OP
        and     loc_processors[MONO_DATA][di].nop_flag,not NF_NOP
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,al
        clc                                       ;'C' clear to show success
        ret


set_procs_color_vect:
        jmp     gfd_acts_like_a_bitmap


;/*
;** The operation was one which could be performed in one pass
;** on the EGA, but the pattern wasn't solid.  If the ROP is
;** either P or Pn and opaque mode is set, then the inner loop
;** can use STOSB with the latches loaded from EGA memory which
;** contains the pattern.  The first and last bytes will have to
;** be done one plane at a time.
;*/

set_procs_ega_50:
        or      bx,bx                             ;BX will be 0 if either
        mov     bl,ch                             ;put C3 color in place, just in case
        jz      set_procs_color_vect              ;  transparent bits or no STOSB_OK
        mov     al,ah                             ;Negate pattern if needed
        cbw
        .errnz  NEG_PATTERN - 10000000b
        xor     dl,ah                             ; Invert C0 pattern
        xor     dh,ah                             ; Invert C1 pattern
        xor     cl,ah                             ; Invert C2 pattern
        xor     ch,ah                             ; Invert C3 pattern

        mov     si,loc_proc_offset
        mov     loc_processors[C0_DATA][si].pattern,dl
        mov     loc_processors[C1_DATA][si].pattern,dh
        mov     loc_processors[C2_DATA][si].pattern,cl
        mov     loc_processors[C3_DATA][si].pattern,ch
        mov     loc_processors[MONO_DATA][si].inner_loop_proc,CodeOFFSET ega_inner_latch
        xor     di,di
        cmp     di,si
        sbb     di,di                            ; DI=FFFF if backgrnd, 0 otherwise
        and     di,CodeOFFSET ega_all_partial_back-CodeOFFSET ega_all_partial
        add     di,CodeOFFSET ega_all_partial
        mov     loc_processors[MONO_DATA][si].first_last_proc,di

        mov     di,arg_lp_ddc.off                ;Place the pattern into EGA memory
        mov     di,[di].ddc_npsd                 ;  at current_brush
        mov     ds,[di].sd_pBits.sel
        assumes ds,EGAMem

        mov     bx,dx                             ;Don't destroy C0 and C1 colors
        lea     di,current_brush[0]               ;Use the first location of the brush
        mov     dx,EGA_BASE + SEQ_DATA            ;Set the pattern into ega memory
        mov     al,MM_C0
        out     dx,al
        mov     [di],bl
        mov     al,MM_C1
        out     dx,al
        mov     [di],bh
        mov     al,MM_C2
        out     dx,al
        mov     [di],cl
        mov     al,MM_C3
        out     dx,al
        mov     [di],ch
        mov     di,si
        jmp     set_procs_ega_40                 ;Finish here


get_fill_data   endp
page

;/***************************************************************************
;*
;* FUNCTION NAME = set_procs_non_ega
;*
;* 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         = BL     =  SEG_MAP_MASK value for this plane if EGA
;*                 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 = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

set_procs_non_ega proc near

        xor     ch,ah                             ;Invert pattern if needed
        mov     ss:[si].pattern,ch
        xor     ch,ah
        mov     ss:[si].plane_map_mask,bl
        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,0FFFFh      ;  then we're done
        je      set_procs_non_ega_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_non_ega_exit:
        add     si,size cntrl_blk                 ;--> next control block structure
        ret

set_procs_non_ega endp
page

;*/
;** The following two bit mask tables are used for fetching
;** the first and last byte used-bits bitmask.
;**
;** So I lied.  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

;/*
;**     dw      1000000000000000B                ;Masks for rightmost byte
;**     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         =
;* OUTPUT        =
;*
;* RETURN-NORMAL = 'C' clear               
;* RETURN-ERROR  = DS:DI --> first point   
;*                 'C' if no more intervals
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

comp_interval_null:
        stc
        ret

        public  comp_interval
comp_interval   proc near

        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 byte/word address of this interval.
;** fetch_data contains the shift count required for either
;** bytes or words.
;*/

        mov     cx,loc_fetch_data                 ;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 byte/word shift count
        cmp     cl,3                              ;Must handle word/byte intervals
        je      cmp_byte_interval                 ;  seperately from this point
        jmp     cmp_word_interval


;/*
;** This is for byte intervals.  The left side mask was not
;** created correctly.  It is off by one entire byte, or 8
;** bit positions.  The right side mask is also incorrect.
;** It must be placed into BL so the 8-bit partial byte code
;** can find it.
;*/

cmp_byte_interval:
        add     di,loc_scan_start.lo              ;DS:DI --> first byte
        mov     al,ah                             ;Set correct first byte mask
        xor     ah,ah
        xchg    bl,bh                             ;Place these in the correct order
        shr     si,cl                             ;Compute inner byte count
        jnz     cmp_byte_interval_10              ;loop count + 1 > 0, check it out

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

        and     al,bl                             ;AL = left, BL = right, AH = 0
        xor     bx,bx                             ;Want the entire mask to be 0
        inc     si                                ;Fall through to set 0

cmp_byte_interval_10:
        dec     si                                ;Dec inner loop count (might become 0)

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

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

        cmp     bl,dl                             ;Set 'C' if not all pixels 1
        sbb     si,dx                             ;If no 'C', sub -1 (add 1), else sub 0
        cmp     bl,dl                             ;Set 'C' if not all pixels 1
        sbb     bl,dl                             ;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)
;*/

comp_interval_save:
        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
        ret

;/*
;** Everything said for bytes will just be repeated for
;** words.
;*/


cmp_word_interval:
        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
        jmp     comp_interval_save                ;Finish here

comp_interval   endp
page


;/***************************************************************************
;*
;* FUNCTION NAME = ega_all_partial
;*
;* DESCRIPTION   = 
;*
;*          ega_all_partial handles the partial byte of a scanline when all
;*          of the following conditions are met:
;*       
;*              a)  The destination is the EGA
;*              b)  The raster op is either P or Pn
;*              c)  No transparency
;*              d)  The brush is non-solid
;*       
;*          All planes will be processed according to the given mask.  If the
;*          rop is Pn, the pattern will have been inverted prior to calling
;*          this routine.
;*
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,DX,FLAGS
;*
;* INPUT         = DS:DI    --> destination byte
;*                 ES:DI    --> destination byte
;*                    AL     =  mask of altered bits
;*                    BX     =  pattern to use (color) for C3
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* OUTPUT        = DS:DI    --> next destination word
;*                 ES:DI    --> next destination word
;*                    CX     =  0
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;**************************************************************************/

        assumes ds,EGAMem
        assumes es,EGAMem

ega_all_partial proc near

        mov     dx,EGA_BASE+GRAF_DATA             ;Set the bitmask register
        out     dx,al                             ;  to those bits which can be altered
        mov     dl,SEQ_DATA

        mov     al,MM_C3
        out     dx,al
        mov     al,bl
        xchg    [di],al
        mov     al,MM_C2
        out     dx,al
        mov     al,loc_processors[C2_DATA].pattern
        xchg    [di],al
        mov     al,MM_C1
        out     dx,al
        mov     al,loc_processors[C1_DATA].pattern
        xchg    [di],al
        mov     al,MM_C0
        out     dx,al
        mov     al,[di]
        mov     al,loc_processors[C0_DATA].pattern
        stosb
        xor     cx,cx                             ;Must return with cx zero
        ret

ega_all_partial endp


;/***************************************************************************
;*
;* FUNCTION NAME = ega_all_partial_back
;*
;* DESCRIPTION   = 
;*
;*       ega_all_partial_back handles the partial byte of a scanline when all
;*       of the following conditions are met:
;*    
;*           a)  The destination is the EGA
;*           b)  The raster op is either P or Pn
;*           c)  No transparency
;*           d)  The brush is non-solid
;*    
;*       All planes will be processed according to the given mask.  If the
;*       rop is Pn, the pattern will have been inverted prior to calling
;*       this routine.
;*       
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,DX,FLAGS
;*
;* INPUT         = DS:DI    --> destination byte
;*                 ES:DI    --> destination byte
;*                    AL     =  mask of altered bits
;*                    BX     =  pattern to use (color) for C3
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* OUTPUT        = DS:DI    --> next destination word
;*                 ES:DI    --> next destination word
;*                    CX     =  0
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,EGAMem
        assumes es,EGAMem

ega_all_partial_back proc near

        mov     dx,EGA_BASE+GRAF_DATA             ;Set the bitmask register
        out     dx,al                             ;  to those bits which can be altered
        mov     dl,SEQ_DATA

        mov     al,MM_C3
        out     dx,al
        mov     al,bl
        xchg    [di],al
        mov     al,MM_C2
        out     dx,al
        mov     al,loc_bk_processors[C2_DATA].pattern
        xchg    [di],al
        mov     al,MM_C1
        out     dx,al
        mov     al,loc_bk_processors[C1_DATA].pattern
        xchg    [di],al
        mov     al,MM_C0
        out     dx,al
        mov     al,[di]
        mov     al,loc_bk_processors[C0_DATA].pattern
        stosb
        xor     cx,cx                             ;Must return with cx zero
        ret

ega_all_partial_back endp
page

;/***************************************************************************
;*
;* FUNCTION NAME = ega_inner_latch
;*
;* DESCRIPTION   = 
;*                 
;*      ega_inner_latch handles the inner loop bytes of a scanline when all
;*      of the following conditions are met:
;*    
;*             a)  The destination is the EGA
;*             b)  The raster op is either P or Pn
;*             c)  No transparency
;*             d)  The brush is non-solid
;*    
;*      The pattern which is stored at EGAMem:current_brush[0] will
;*      be loaded into the latches and stored into the destination.
;*
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,DX,FLAGS
;*
;* INPUT         = DS:DI    --> destination byte
;*                 ES:DI    --> destination byte
;*                    AL     =  0FFh
;*                    CX     =  byte count
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* OUTPUT        = DS:DI    --> next destination word
;*                 ES:DI    --> next destination word
;*                    CX     =  0
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,EGAMem
        assumes es,EGAMem

ega_inner_latch proc near

        mov     dx,EGA_BASE+SEQ_DATA
        mov     al,MM_ALL
        out     dx,al
        mov     dl,GRAF_DATA
        xor     ax,ax                             ;Setting all bits to 0 will cause
        out     dx,al                             ;  the latches to be used
        mov     al,current_brush[0]               ;Load the latches with the pattern
        rep     stosb
        ret

ega_inner_latch endp

page
;/***************************************************************************
;*
;* FUNCTION NAME = ega_inner_stosb
;*
;* DESCRIPTION   = 
;*                 
;*       ega_inner_stosb handles the inner loop bytes of a scanline when all
;*       of the following conditions are met:
;*
;*           a)  The destination is the EGA
;*           b)  No transparency
;*           c)  rep stosb can be used
;*
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,DX,FLAGS
;*
;* INPUT         = DS:DI    --> destination byte
;*                 ES:DI    --> destination byte
;*                    AL     =  0FFh
;*                    CX     =  byte count
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* OUTPUT        = DS:DI    --> next destination word
;*                 ES:DI    --> next destination word
;*                    CX     =  0
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,EGAMem
        assumes es,EGAMem

ega_inner_stosb proc near

        mov     dx,EGA_BASE+GRAF_DATA            ;Transparency mask must be FF
        out     dx,al
        rep     stosb
        ret

ega_inner_stosb endp
page
;/***************************************************************************
;*
;* FUNCTION NAME = ega_first_last
;*
;* DESCRIPTION   = 
;*                 
;*         ega_first_last handles the first and last bytes of the raster
;*         operations which can be performed in one pass, and the inner
;*         loop of those raster operations which can be performed in one
;*         pass that cannot use stosb.
;*      
;*         The following conditions must be met:
;*      
;*             a)  The destination is the EGA
;*             b)  single pass rop
;*             c)  rep stosb cannot be used if for the inner loop
;*      
;*         This function can handle both opaque and transparent modes.
;*
;*                 Registers Preserved:
;*                       BX,SI,DS,ES,BP
;*                 Registers Destroyed:
;*                       AX,DX,FLAGS
;*
;* INPUT         = DS:DI    --> destination byte
;*                 ES:DI    --> destination byte
;*                    AL     =  transparency/first/last mask as appropriate 
;*                    CX     =  byte count
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* OUTPUT        = DS:DI    --> next destination word
;*                 ES:DI    --> next destination word
;*                    CX     =  0
;*                 GRAF_ADDR =  GRAF_BIT_MASK
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,EGAMem
        assumes es,EGAMem

ega_first_last  proc near

        mov     dx,EGA_BASE+GRAF_DATA
        out     dx,al
        xchg    ax,si
        mov     si,di                             ;Must not destroy SI
        rep     movsb
        xchg    si,ax
        ret

ega_first_last  endp

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.
;*                 
;*                 Registers Preserved:
;*                       BX,SI,BP,DS,ES
;*                 Registers Destroyed:
;*                       AX,DX,flags
;*
;* INPUT         = AX = transparency mask
;*                 BX = pattern
;*                 CX = loop count
;*                 SI = index into table of per-plane processor blocks
;*                 DS:DI --> destination
;*                 ES:DI --> destination
;*                 direction flag cleared
;*
;* OUTPUT        = CX = 0
;*                 DS:DI --> next destination
;*                 ES:DI --> next destination
;*                 direction flag cleared
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*  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

do_nothing      proc    near
        ret
do_nothing      endp

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
