;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = CBLT.ASM
;*
;* DESCRIPTIVE NAME = Compile a BLT subroutine onto the stack
;*
;*
;* VERSION      V2.0
;*
;* DATE         
;*
;* DESCRIPTION  This file contains two subroutines which build a small program on 
;*              the stack to accomplish the requested BLT.
;*                                                                                     
;*              This file is part of a set that makes up the BitBLT function   
;*              at driver-level.                                                       
;*
;* FUNCTIONS    CBLT 
;*              phase_align_generate
;*              y_update 
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   02/22/8?                     Walt Moore [waltm] Wrote it for         in
;*                                distant past.
;*   07/12/86                     Wes Rupel [wesleyr] Made it a subroutine 
;*                                (extracted from an enourmous bitblt.asm)
;*   07/20/87                     Wes Rupel [wesleyr] Added 4-plane support.
;*   08/16/87                     Wes Rupel [wesleyr] Bitmap Color Conversion 
;*                                uses image color
;*   03/05/88                     Wes Rupel [wesleyr] Added Gray usMix support.
;*   03/05/88                     Wes Rupel [wesleyr] Subroutinized it so that
;*                                mono_to_color coversion can optionally be done
;*                                after phase align (needed for transparency).
;*   03/07/88                     Wes Rupel [wesleyr] Added Transparency for 
;*                                ImageData
;*   03/30/88                     Wes Rupel [wesleyr] Made BackColor/ForeColor 
;*                                now 0/1 rather than 1/0 (in mono bitmaps)
;*   10/26/89                     Viroon Touranachun [viroont] Modified the part 
;*                                that generates the "REP" inner loop for 
;*                                "Source Copy" to generate 32-bit data transfer 
;*                                routine using iAPX386's MOVSD and STOSD 
;*                                instructions.
;*
;*****************************************************************************/

        .errnz  BITS_PEL - 4                      ;some uses of BITS_PEL expect it
                                                  ;to be 4  --  should clean up

        .xlist
        include cmacros.inc
        include pmgre.inc
DINCL_BB_ROPS   equ     1
        include driver.inc
        include display.inc
        include egafam.inc
        include egamemf.inc
        include assert.mac
        include njmp.mac
        include oemblt.inc
        .list

        externA COLOR_DONT_CARE                   ; for use with GRAF_CDC register

sBegin  PtrData
        externB shadowed_graf_mode
sEnd    PtrData

sBegin  BitBlt
        assumes cs,BitBlt

        externW roptable
        externW MyPtrBitBltData

        include constant.blt

        I_USE_386   equ     066h
        I_ROR_AX_N  equ     0C8C1h

;/*
;** Don't DWORD align REP MOVSD if the number of bytes to transfer is too
;** small.  Alignment on the average will require 2 MOVSB's at a cost of 14
;** clocks.  Misalignment costs 1? clocks per byte.
;*/
          TOO_SMALL_TO_ALIGN equ 14

        include devdata.blt
DEFINE_ROPCODE = 1
        include roptable.blt                      ; generate code

;/*
;**     jmp_cx_nz   - Code template for near jump if CX-1 <> 0
;**
;**     jmp_cx_nz will skip the following near jump if CX-1 is zero.
;**     CX will be left updated by this code.
;**
;**     jmp_cx_nz is used by both the inner loop code and the outer
;**     loop code if a loop instruction cannot be used.
;*/

jmp_cx_nz:
        dec     cx                                ;Decrement counter
        jz      $+5
        db      I_JMP_NEAR                        ;JMP opcode

JMP_CX_NZ_LEN   equ     $-jmp_cx_nz               ;Length of procedure


;/*
;**     phase_align - Template for phase alignment code
;**
;**     The following code is the template that performs the phase
;**     alignment masking.  The source has already been aligned to
;**     the destination.
;**
;**     A copy of the aligned source is made.  The phase mask is then
;**     applied to the source and the copy.  The previously unused
;**     bits are ORed into the used bits of the current source, and
;**     the unused bits of the current source then become the unused
;**     bits for the next source.
;**
;**
;**     It assumes:
;**
;**             BP  =  phase alignment mask
;**             AL  =  current byte to mask
;**             BH  =  old unused bits
;*/

phase_align:
        mov     ah,al                             ;Make a copy of aligned source
        and     ax,bp                             ;Masked used, unused bits
        or      al,bh                             ;Mask in old unused bits
        mov     bh,ah                             ;Save new unused bits

PHASE_ALIGN_LEN equ     $-phase_align             ;Length of procedure


;/*
;**     masked_store - Template for storing first and last bytes of BLT
;**
;**     The following code is a template for storing the first and last
;**     bytes of a BLT.  The unaltered bits are saved and the altered
;**     bits set in the byte, then the byte is stored.
;**
;**
;**     It assumes:
;**
;**             AL  =  The byte to be BLTed to the destination bitmap.
;**                    All necessary logic operations have been performed
;**                    on this byte.
;**
;**             AH  =  The destination byte.
;**
;**     The AND immediate will be fixed up.
;*/

masked_store:
        and     ax,0FFFFh                         ;Mask altered/unaltered bits
        or      al,ah                             ;Combine the bits
        stosb                                     ;And store the result

MASKED_STORE_LEN        equ                      $-masked_store ;Length of the template
MASKED_STORE_MASK       equ                      -5             ;Offset to where mask goes

;/*
;** Gray usMix logical action template.  This is used instead of one of the
;** roptable rops when the GRAY_ROP MIX requested.
;*/

gray_rop_template:
        mov     ah,es:[di]
        mov     al,ah
        xor     al,dl
        and     al,dh
        xor     al,ah
LENGTH_GRAY_ROP_TEMPLATE      =       $ - gray_rop_template

;/*
;**  transparency_template is a piece of code that will be appended to the
;**  logical action template from the roptable to achieve transparency.
;**  The transparency mask is assumed to be in DH.  Where the mask is "1"
;**  the result of the logical action is used.  Where the mask is "0"
;**  the destination is not altered.
;/*

transparency_template:
        mov     ah,es:[di]
        xor     al,ah
        and     al,dh
        xor     al,ah
LENGTH_TRANSPARENCY_TEMPLATE                       =   $ -    transparency_template

        page
;/*
;**
;**     Pattern Fetch Code
;**
;**     The pattern fetch code will be created on the fly since
;**     most of the instructions need fixups.
;**
;**     This template is really just a comment to indicate what
;**     the generated code should look like.
;**
;**     Entry:  None
;**
;**     Exit:   DH = pattern
;**
;**     Uses:   AX,BX,CX,DH,flags
;**
;**
;**     The following registers are available to the pattern fetch
;**     logic (as implemented herein):
;**
;**             AX,BX,CX,DX,flags
;**
;**
;**     For monochrome brushes:
;**
;**         mov     ax,1234h                      ;Load segment of the brush
;**         mov     bx,1234h                      ;Load offset of the brush
;**         mov     cx,ds                         ;Save DS
;**         mov     ds,ax                         ;DS:BX --> brush
;**         mov     dh,7[bx]                      ;Get next brush byte
;**         mov     al,ss:[1234h]                ;Get brush index
;**         add     al,iDir                       ;Add displacement to next byte (+1/-1)
;**         and     al,00000111b                 ;Keep it in range
;**         mov     ss:[1234h],al                ;Store displacement to next byte
;**         mov     ds,cx                         ;Restore DS
;**
;**
;**     For color brushes:
;**
;**         mov     ax,1234h                      ;Load segment of the brush
;**         mov     bx,1234h                      ;Load offset of the brush
;**         mov     cx,ds                         ;Save DS
;**         mov     ds,ax                         ;DS:BX --> brush
;**         mov     dh,7[bx]                      ;Get next brush byte
;**         mov     al,ss:[1234h]                ;Get brush index
;**         add     al,SIZE Pattern              ;Add disp. to next plane's bits
;**         and     al,00011111b                 ;Keep it within the brush
;**         mov     ss:[1234h],al                ;Store disp. to next plane's bits
;**         mov     ds,cx                         ;Restore DS
;**
;**
;**     For both templates, SS:[1234] is the address of the 7 in the
;**     "mov dh,7[bx]" instruction.  This is the index to this scan's
;**     bit pattern in the brush.  This value will range from 0 to
;**     (SIZE pattern)-1 for monochrome devices, and from 0 to
;**     ((NumberPlanes)*(SIZE pattern))-1 for color devices.
;**
;**     For color brushes, SS:[1234] must also be fixed up when the next
;**     scan line is selected, else it would index into the monochrome
;**     portion of the brush (e.g. 1,9,17,25, where 25 is not part of the
;**     color brush).
;*/


;/***************************************************************************
;*
;* FUNCTION NAME = CBLT
;*
;* DESCRIPTION   = Compile a BLT onto the stack.  
;*
;* INPUT         = ES:DI --> memory on stack to receive BLT program  
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

;/*
;**       Note:   The definition of CBLT below is FAR in order to maintain
;**               the stack frame created for BITBLT, though it is reached
;**               with a NEAR call.
;*/

cProc   CBLT,<FAR,PUBLIC,NODATA>,<si,di>
        include frame.blt
cBegin  <nogen>

        mov     ax,cs                             ;Set data seg to CS so we can access
        mov     ds,ax                             ;  code without overrides
        assumes ds,Code

;/*
;**       Initialize plane indicator.
;*/

        mov     ax,(PLANE_SELECT_FLAG shl 8)+I_MOV_BL_BYTE_I
        stosw

        subttl  Compile - Outer Loop
        page

;/*
;**     Create the outerloop code.  The first part of this code will save
;**     the scan line count register, destination pointer, and the source
;**     pointer (if there is a source).
;**
;**
;**     The generated code should look like:
;**
;**             push    cx                        ;Save scan line count
;**             push    di                        ;Save destination pointer
;**     <       push    si                       > ;Save source pointer
;*/

        mov     bl,fbF0
        mov     ax,I_PUSH_CX_PUSH_DI              ;Save scan line count, destination ptr
        stosw
        test    bl,F0_SRC_PRESENT                 ;Is a source needed?
        jz      cblt_2020                         ;  No
        mov     al,I_PUSH_SI                      ;  Yes, save source pointer
        stosb

cblt_2020:



        subttl  Compile - Plane Selection
        page

;/*
;**     If the destination device is color and the display is involved in
;**     the blt, then the color plane selection logic must be added in.
;**     If the destination is monochrome, then no plane logic is needed.
;**     Two color memory bitmaps will not cause the plane selection logic
;**     to be copied.
;**
;**
;**     The generated code should look like:
;**
;**     <       push    bx                       > ;Save plane index
;**     <       plane selection >                ;Select plane
;*/


        test    bl,F0_DEST_IS_COLOR               ;Is the destination color?
        jz      cblt_pattern_fetch                ;  No
        mov     al,I_PUSH_BX                      ;Save plane index
        stosb
        test    bl,F0_DEST_IS_DEV+F0_SRC_IS_DEV   ;Is the device involved?
        jz      cblt_pattern_fetch                ;  No


;/*
;**       The device is involved for a color blt.  Copy the logic for selecting
;**       the read/write plane, and perform any fixups that are needed.
;*/

        mov     si,BitBltOFFSET cps                ;--> plane select logic

        CPUMode 386
if      LENGTH_CPS GE 8
        mov     cx,LENGTH_CPS/4
        rep     movsd
else
if      LENGTH_CPS AND 4
        movsd
endif
endif
if      LENGTH_CPS AND 2
        movsw
endif
if      LENGTH_CPS AND 1
        movsb
endif
        CPUMode 286


        subttl  Compile - Pattern Fetch
        page

;/*
;**   Set up any pattern fetch code that might be needed.
;**   The pattern code has many fixups, so it isn't taken from a
;**   template.  It is just stuffed as it is created.
;**
;**
;**   Entry:  None
;**
;**   Exit:   DH = pattern
;**
;**   Uses:   AX,BX,CX,DH,flags
;**
;**
;**   For color brushes:
;**
;**       mov     bx,YYYYh                             ;Load offset (immediate) of the brush
;**    *  mov     ax,XXXXh                             ;Load segment (immediate) of the brush
;**    *  mov     cx,ds                                ;Save DS
;**    *  mov     ds,ax                                ;DS:BX --> brush
;**       mov     dh,7[bx]                             ;Get next brush byte
;**       mov     al,ss:[xxxxh]                        ;Get brush index
;**       add     al,SIZE Pattern     ;Add displacement to next plane's bits
;**       and     al,00011111b                         ;Keep it within the brush
;**       mov     ss:[xxxxh],al                        ;Store displacement to next plane's bits
;**    *  mov     ds,cx                                ;Restore DS
;**
;**   For monochrome brushes:
;**
;**       mov     bx,YYYYh                             ;Load offset (immediate) of the brush
;**    *  mov     ax,XXXXh                             ;Load segment (immediate) of the brush
;**    *  mov     cx,ds                                ;Save DS
;**    *  mov     ds,ax                                ;DS:BX --> brush
;**       mov     dh,7[bx]                             ;Get next brush byte
;**       mov     al,ss:[xxxxh]                        ;Get brush index
;**       add     al,iDir                              ;Add displacement to next byte (+1/-1)
;**       and     al,00000111b                         ;Keep it in range
;**       mov     ss:[xxxxh],al                        ;Store displacement to next byte
;**    *  mov     ds,cx                                ;Restore DS
;**
;**
;**   For masks:
;**
;**       The Gray usMix uses the transparency mask on all planes.
;**       So the fetch logic is the same as for a monochrome brush
;**       except that we don't increment to the next brush byte until
;**       we have done all the planes.  So we will cut out the increment
;**       from the monochrome fetch code and leave it to the "next scanline"
;**       code at the very bottom to update the brush index after all the planes
;**       are done.
;**
;**       mov     bx,YYYYh                             ;Load offset (immediate) of the brush
;**    *  mov     ax,XXXXh                             ;Load segment (immediate) of the brush
;**    *  mov     cx,ds                                ;Save DS
;**    *  mov     ds,ax                                ;DS:BX --> brush
;**       mov     dh,7[bx]                             ;Get next brush byte
;**    *  mov     ds,cx                                ;Restore DS
;**
;**
;**       The address of the increment for the brush is saved for
;**       the plane looping logic if the destination is a three plane
;**       color device.  For a four plane color device, the AND
;**       automatically handles the wrap and no fixup is needed at
;**       the end of the plane loop.
;**
;**       Instructions marked with "*" are not present if there is no
;**       source bitmap in the rop.  The bitmap is what DS would otherwise
;**       be used for.
;*/

cblt_pattern_fetch:
        test    bl,F0_PAT_PRESENT                ;Is a pattern needed?
        njz     cblt_initial_byte_fetch          ;  No, skip pattern code


;/*
;** The special gray rop will erroneously lead to Color Pat Fetch. 
;** It should be mono fetch of the transparency mask.   
;*/
                                                      
        test    fsBlt,BBF_GRAY_ROP                   
        jz      cblt_not_gray_rop                    
                                                      
color_fetch_template:                                 
        and     fbF0,not F0_COLOR_PAT            ; really a mono (1 plane) fetch
        and     bl,not F0_COLOR_PAT              ; really a mono (1 plane) fetch
;/*
;** Take this oportunity to compile the "color fetch" code needed by the
;** Gray usMix.  This will compile code which will expand the background color bit
;** for the current plane into all of DL.  It will look like this for color 03h:
;**
;**   mov   dl,03h   ; grab the background color as an immediate value.
;**   and   dl,bl    ; BL is the plane mask.  This isolates the color bit for
;**   neg   dl       ; the current plane.  The NEG set the carry if the bit was
;**   sbb   dl,dl    ; one.  SBB sets DL to 0 or FF depending on the carry.
;**

        CPUMode 386

        mov     eax,(I_AND_DL_BL shl 16)+I_MOV_DL_BYTE_I ; mov dl,PatBackColor
        mov     ah,ipcBrushBack.ipc_bClr                 ; and dl,bl
        stosd
        mov     eax,(I_SBB_DL_DL shl 16)+I_NEG_DL        ; neg dl
        stosd                                            ; sbb dl,dl

        CPUMode 286

cblt_not_gray_rop:                                    

        mov     al,I_MOV_BX_WORD_I               ;mov bx,pBrush.lo
        stosb
        mov     ax,pBrush.off
        stosw

        test    fbF0,F0_SRC_PRESENT ;Is there a source?   
        jz      cbltpf_no_source                     

        mov     al,I_MOV_AX_WORD_I               ;mov ax,pBrush.hi
        stosb
        mov     ax,pBrush.sel
        stosw

        CPUMode 386

        mov     eax,(I_MOV_DS_AX shl 16) + I_MOV_CX_DS    ;mov cx,ds
        stosd                                             ;mov ds,ax

        CPUMode 286

cbltpf_no_source:
        mov     ax,I_MOV_DH_BX_DISP8              ;mov dh,yPatRow[bx]
        stosw

;/*
;** apparently we don't need to save npbPatRow if it is a normal
;** mono fetch, but the Gray usMix mono fetch requires it.
;*/

        mov     npbPatRow,di                      ;Save address of the brush index
        mov     dx,di                             ;Save address of the brush index
        mov     al,yPatRow                        ;Set initial pattern row
        mov     bh,00000111b                      ;Set brush index mask
        and     al,bh                             ;Make sure it's legal at start
        stosb

;/*
;** If we are using the transparency mask (as with Gray usMix) then we ;
;** don't want to update the brush index until we've done all 4 planes. ;
;** This updating will be done at the bottom after we finish the plane ;
;** select loop.                                        ;
;/*
        test    fsBlt,BBF_GRAY_ROP                   ; 
        jnz     cblt_using_mask                      ; 

        mov     ax,I_SS_OVERRIDE+(I_MOV_AL_MEM*256)
        stosw                                     ;mov al,ss:[xxxx]
        mov     ax,dx
        stosw
        mov     al,I_ADD_AL_BYTE_I
        mov     ah,iDir                           ;Set brush index
        .errnz   INCREASE - 1                     ;Must be a 1
        .errnz   DECREASE + 1                     ;Must be a -1

        test    bl,F0_COLOR_PAT                   ;Color pattern required?
        jz      cblt_mono_pat                     ;  No
        mov     ah,SIZE_PATTERN                   ;Set increment to next plane
        mov     bh,00011111b                      ;Set brush index mask

cblt_mono_pat:
        stosw

        CPUMode 386
        mov     eax,((I_SS_OVERRIDE+(I_MOV_MEM_AL*256)) shl 16) + I_AND_AL_BYTE_I
        mov     ah,bh                             ;and al,BrushIndexMask
        stosd                                     ;mov ss:[xxxx],al
        CPUMode 286

        mov     ax,dx
        stosw

cblt_using_mask:

        test    bl,F0_SRC_PRESENT ;Is there a source?   
        jz      cbltpf_no_source_here                

        mov     ax,I_MOV_DS_CX                    ;mov ds,cx
        stosw

cbltpf_no_source_here:


        subttl  Compile - Initial Byte Fetch
        page

;/*
;**     Create the initial byte code.  This may consist of one or two
;**     initial fetches (if there is a source), followed by the required
;**     logic action.  The code should look something like:
;**
;**     BLTouterloop:
;**     <       mov     bp,mPhase   >            ;Load phase mask for entire loop
;**     <       xor     bh,bh                        > ;Clear previous unused bits
;**
;**     ;       Perform first byte fetch
;**
;**     <       lodsb                                > ;Get source byte
;**     <       color<==>mono munge >            ;Color <==> mono conversion
;**     <       phase alignment     >            ;Align bits as needed
;**
;**     ;       If an optional second fetch is needed, perform one
;**
;**     <       lodsb                                > ;Get source byte
;**     <       color to mono munge >            ;Color to mono munging
;**     <       phase alignment     >            ;Align bits as needed
;**
;**             logical action                    ;Perform logical action required
;**
;**             mov     ah,es:[di]               ;Get destination
;**             and     ax,cx                     ;Saved unaltered bits
;**             or      al,ah                     ;  and mask in altered bits
;**             stosb                             ;Save the result
;**
;**
;**     The starting address of the first fetch/logical combination will be
;**     saved so that the code can be copied later instead of recreating it
;**     (if there are two fecthes, the first fetch will not be copied)
;**
;**     The length of the code up to the masking for altered/unaltered bits
;**     will be saved so the code can be copied into the inner loop.
;*/


cblt_initial_byte_fetch:


        mov     cFetchCode,1                      ; default is 1 == SIZE(lodsb)
        xor     dx,dx
        or      dh,iHorzPhase                     ; Is the phase 0? (also get the phase)
        jz      cblt_3020                         ;   Yes, so no phase alignment needed
        mov     al,I_MOV_BP_WORD_I                ; Set up the phase mask
        stosb
        mov     ax,mPhase                         ; Place the mask into the instruction
        stosw


        mov     ax,I_XOR_BH_BH                    ; Clear previous unused bits
        stosw

cblt_3020:
        mov     npFetchStart,di                   ;Save starting address of action
        test    bl,F0_SRC_PRESENT                 ;Is there a source?
        jnz     cblt_src_is_present               ;  Yes, generate fetch code
        jmp     cblt_logical_action               ;  No, don't generate fetch code

cblt_src_is_present:

;/*
;**     Generate the required sequence of instructions for a fetch
;**     sequence.  Only the minimum code required is generated.
;**
;**     The code generated will look something like the following:
;**
;**     BLTfetch:
;**     <       lodsb                                  > ;Get the next byte
;**     <       color munging                          > ;Mono <==> color munging
;**
;**     ;       If the phase alignment isn't zero, then generate the minimum
;**     ;       phase alignment needed.  RORs or ROLs will be generated,
;**     ;       depending on the fastest sequence.  If the phase alignment
;**     ;       is zero, than no phase alignment code will be generated.
;**
;**     <       ror     al,1                           > ;Rotate as needed
;**     <       ror     al,1                           > ;Rotate as needed
;**     <       ror     al,1                           > ;Rotate as needed
;**     <       ror     al,1                           > ;Rotate as needed
;**     <       mov     ah,al                          > ;Mask used, unused bits
;**     <       and     ax,bp                          > ;(BP) = phase mask
;**     <       or      al,bh                          > ;Mask in old unused bits
;**     <       mov     bh,ah                          > ;Save new unused bits
;**
;**
;**     The nice thing about the above is it is possible for the fetch to
;**     degenerate into a simple LODSB instruction.
;**
;**     If this was a iAPX80286 implementation, if would be faster to
;**     make three or four rotates into a "ror al,n" instruction.
;**
;**     Currently:      BL = fbF0
;**                     BH = usMix[3]
;*/

        mov     bh,byte ptr fsBlt[1]              ; We will test BH several times
        .errnz  low BBF_TRANS
        .errnz  low BBF_ANTI_TRANS
        .errnz  low BBF_TRANS_NO_CC
        test    bl,F0_GAG_CHOKE                   ; Color conversion?
        jnz     cblt_color_convert                

;/*
;**   No Color Conversion.
;**   If it is mono to mono transparent then we want to go filter thru the
;**   first part of the mono_to_color code just to get the
;**   tranparency stuff.  Being here means no color conversion and transparent
;**   means mono source, so if it is transparent than it is mono to mono.
;/*

        test    bh,(BBF_TRANS or BBF_ANTI_TRANS) shr 8
        jnz     cblt_gotta_transparency           ; it's mono_to_mono transparency
        jmp     cblt_no_color_conversion

cblt_color_convert:
        mov     fbMore,0                          ; Assume REP cannot be used
        test    bl,F0_SRC_IS_COLOR
        jnz     cblt_color_to_mono
cblt_gotta_transparency:
        jmp     cblt_mono_to_color


        subttl  Compile - Initial Byte Fetch, Color ==> Mono
        page

cblt_color_to_mono:

;/*
;**     We examined the target image data colors when we discovered
;**     this was color==>mono, and mapped the mix into DDx or DDxn
;**     if the target image colors were the same.  Therefore, once
;**     we converted the source to monochrome, we can either just
;**     store it or invert it and store it.
;**
;**     Generate the code to go from color to mono.  Color to mono
;**     should map all colors that are the passed background to the
;**     image data background color, and all other colors to the image
;**     data foreground color.
;**
;**     If the source is the display, the color compare register will
;**     be used to do the actual comparison.  We might have to invert
;**     the results depending on the image data colors.
;**
;**     If the source is a memory bitmap, each byte of the plane will be
;**     XORed with the color from that plane, with the results all ORed
;**     together giving the desired result.
;**
;**     The generated code for bitmaps should look something like:
;**
;**         mov     al,[si].next_plane             ; Get C1 byte of source
;**         mov     ah,[si].(2*next_plane)        ; Get C2 byte of source
;**         xor     ax,C1BkColor+(C2BkColor*256) ; XOR with plane's color
;**         or      ah,al                           ; OR the result
;**         mov     al,[si].(3*next_plane)        ; Get C3 byte of source
;**         xor     al,C3BkColor
;**         or      ah,al
;**         lodsb                                 ; Get C0 source
;**         xor     al,C0BkColor                 ; XOR with C0BkColor
;**         or      al,ah                         ; OR with previous result
;**         not     al                            ; NOT to give 1's where background
;** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
;**Currently:
;**             DH = phase
;**             CX = ?
;*/

        test    bl,F0_SRC_IS_DEV                 ; If device, use color compare register
        jz      cblt_clrconv_memory_bitmap       ; It's a memory bitmap

;/*
;**       We're in luck, the color compare register can be used.  Set up
;**       for a color read, and use the normal mono fetch code.  Show the
;**       innerloop code that the REP instruction can be used if this is
;**       a source copy.
;/*

        mov     cx,dx                            ; Save dx
        mov     ah,ipcBkgnd.ipc_bClr             ; Get background color
        mov     al,GRAF_COL_COMP                 ; Stuff color into compare register
        mov     dx,EGA_BASE+GRAF_ADDR
        out     dx,ax
        mov     ax,COLOR_DONT_CARE               ; Set Color Don't Care register
        out16   dx,ax

        push    ds
        mov     ds,MyPtrBitBltData               ; Show color read mode to the
        assumes ds,PtrData                       ;   EGA restoration code

        mov     ax,M_COLOR_READ SHL 8 + GRAF_MODE
        mov     shadowed_graf_mode.vvr_value,ah ; Must shadow this for state code
        out16   dx,ax
        pop     ds
        assumes ds,nothing

        mov     dx,cx
        mov     al,I_LODSB                        ;Generate source fetch
        stosb
        or      fbMore,F1_REP_OK                  ;Assume we can use rep
        mov     cFetchCode,1                      ;Only 1 byte so far
        test    ipcImageBack,MONO_BIT             ;Is background color a 1
        jnz     @F                                ;  Yes, we can use rep
        mov     ax,I_NOT_AL                       ; map  background color to 0's
        stosw                                     ; can ColorCompareReg do this for us?
                                                  ; if so then F1_REP_OK can still be set
        mov     cFetchCode,3
        and     fbMore,not F1_REP_OK
@@:
        jmp     cblt_3160                         ; Go create phase & logic code

cblt_clrconv_memory_bitmap:

;/*
;**       The source is a memory bitmap.           Generate the code to compute
;**       the result of the four planes:
;*/
        mov     cx,di                             ; save initial di for fetch size calc.
        mov     ax,I_MOV_AL_SI_DISP16
        stosw
        mov     ax,devSrc.next_plane
        stosw
        xchg    bx,ax                             ; xchg is one byte less than mov
        add     bx,bx
        mov     ax,I_MOV_AH_SI_DISP16
        stosw
        mov     ax,bx
        stosw
        mov     al,I_XOR_AX_WORD_I
        stosb
        mov     al,ipcBkgnd.ipc_bClr             ;Get background color
        mov     ah,al
        and     ax,(C2_BIT shl 8) or C1_BIT
        neg     al
        sbb     al,al
        neg     ah
        sbb     ah,ah
        stosw
;/*
;**------------------------------ do 4th plane
;*/

        CPUMode 386

        mov     eax,(I_MOV_AL_SI_DISP16 shl 16)+I_OR_AH_AL
        stosd
        mov     ax,devSrc.next_plane
        add     ax,bx
        stosw
        mov     eax,(I_OR_AH_AL shl 16)+I_XOR_AL_BYTE_I
        mov     ah,ipcBkgnd.ipc_bClr             ;Get background color
        and     ah,C3_BIT
        neg     ah
        sbb     ah,ah
        stosd

        CPUMode 286

        mov     ax,I_LODSB+(I_XOR_AL_BYTE_I*256)
        stosw
        mov     al,ipcBkgnd.ipc_bClr             ;Get background color
        shr     al,1
        sbb     al,al
        .errnz  C0_BIT-00000001b
        stosb
        mov     ax,I_OR_AL_AH
        stosw

        test    ipcImageBack,MONO_BIT             ;Is background color a 0
        jz      @F                                ;  Yes, don't need to invert it
        mov     ax,I_NOT_AL                       ;Map background color to 1's
        stosw
@@:
        sub     cx,di                             ;Compute size of fetch code.
        neg     cx
        mov     cFetchCode,cx
        jmp     cblt_3160                         ;Go create phase/logic code



        subttl  Compile - Initial Byte Fetch, Mono ==> Color
        page

;/*
;**     The conversion is mono to color.  Generate the code to
;**     do the conversion, and generate the table which will
;**     have the conversion values in it.
;**
;**     When going from mono to color, 0 bits are considered to be
;**     the background color, and 1 bits are considered to be the
;**     foreground color.
;**
;**     For each plane:
;**
;**       If the foreground=background=1, then 1 can be used in
;**       place of the source.
;**
;**       If the foreground=background=0, then 0 can be used in
;**       place of the source.
;**
;**       If the foreground=1 and background=0, then the source
;**       can be used as is.
;**
;**       If the foreground=0 and background=1, then the source
;**       must be complemented before using.
;**
;**       Looks like a boolean function to me.
;**
;**     An AND mask and an XOR mask will be computed for each plane,
;**     based on the above.  The source will then be processed against
;**     the table.  The generated code should look like
;**
;**             lodsb
;**             and     al,ss:[xxxx]
;**             xor     al,ss:[xxxx+1]
;**
;**
;**     The table for munging the colors as stated above should look like:
;**
;**          BackGnd   ForeGnd                     Result    AND  XOR
;**             1         1                          1      00   FF
;**             0         0                          0      00   00
;**             1         0                      not S      FF   FF
;**             0         1                          S      FF   00
;**
;**     From this, it can be seen that the XOR mask is the same as the
;**     background color.  The AND mask is the XOR of the foreground
;**     and the background color.  Not too hard to compute
;**
;**
;**     It can also be seen that if the background color is black and the
;**     foreground (text) color is white, then the conversion needn't be
;**     generated (it just gives the source).  This is advantageous since
;**     it will allow phased aligned source copies to use REP MOVSW.
;**
;**
;**     Currently:      ds:si --> bkColor
;*/

cblt_mono_to_color:

;/*
;** Generate the code for munging the color as stated above.
;*/
        mov     al,I_LODSB
        stosb
;/*
;** To make transparent rops needed for ImageData we need to do the phase
;** alignment before the color conversion.  This only applies to mono memory
;** bitmaps being blted to the screen, so we will only do it here -- it is too
;** much trouble to do the phase alignment before color converion in general.
;*/

        push    si
        call    phase_align_generate
        pop     si

;/*
;** if transparency then the compiled code must save the source     ;
;** (now that it has been phase aligned) to use as a mask.  It will save ;
;** it in DH (where the pattern would be if there was one -- transparency ;
;** is being added for ImageData which does not use a pattern).  ;
;/*
                                                  
        test    bh,(BBF_TRANS or BBF_ANTI_TRANS) shr 8
        jz      cblt_no_transparency_needed
        mov     ax,I_MOV_DH_AL                    ; op code for  mov al,dl
        stosw
        test    bh,BBF_ANTI_TRANS shr 8
        jz      cblt_its_not_anti_transparency
        mov     ax,I_NOT_DH                       ; op code for  not dl
        stosw
cblt_its_not_anti_transparency:
        test    bh,BBF_TRANS_NO_CC shr 8
        jnz     already_phase_aligned_load_ds     ; don't color convert
cblt_no_transparency_needed:

;/*
;** Normally being here means the dest is color.   However, we also come
;** thru here just to get the transparency stuff above when doing mono to
;** mono.  It is now time to bounce outta here if that's what we're here for
;** since we don't actually want any mono_to_color conversion code.
;/*

        test    bl,F0_DEST_IS_COLOR
        jz      already_phase_aligned_load_ds    ; it's a mono dest

;/*
;** Now we'll check to see if the background color is black, and the
;** foreground color is white.  This can be determined by looking at
;** the accelerator flags in the physical color.
;*/

;/*
;**!!! Need to set the ONES_OR_ZEROS accelerator
;*/

        mov     ah,ipcImageBack.ipc_bClr   ; get background color
        xor     ah,MONO_BIT                ; Map black to white
        and     ah,ipcImageFore.ipc_bClr   ; AND in foreground color
        cmp     ah,MONO_BIT+ONES_OR_ZEROS
        jne     cblt_not_black

;/*
;** A normal fetch without any color conversion will be used because with
;** black and white as our two colors the color conversion code would just map
;** ones to ones and zeros to zeros.  Show reps as ok, no color munge table.
;*/
        mov     fbMore,F1_REP_OK + F1_NO_MUNGE
        jmp     short already_phase_aligned_load_ds
cblt_not_black:

;/*
;** No way around it.  The color conversion table and code must be generated.
;**
;** Generate the XOR and the AND masks from the foreground and background colors.
;*/

;/*
;**  The XOR mask is the same as the background color.
;**  The AND mask is the XOR of the foreground and the background color.
;*/
        mov     cx,ipcImageBoth                  ;CH = background, CL = foreground
        xor     cl,ch
        shr     cl,1
        sbb     al,al
        shr     ch,1
        sbb     ah,ah
        mov     word ptr ausMono2Color[0],ax
        shr     cl,1
        sbb     al,al
        shr     ch,1
        sbb     ah,ah
        mov     word ptr ausMono2Color[2],ax
        shr     cl,1
        sbb     al,al
        shr     ch,1
        sbb     ah,ah
        mov     word ptr ausMono2Color[4],ax
        shr     cl,1
        sbb     al,al
        shr     ch,1
        sbb     ah,ah
        mov     word ptr ausMono2Color[6],ax

        mov     al,I_SS_OVERRIDE
        stosb
        mov     ax,I_AND_AL_MEM                   ;and al,ss:[xxxx]
        stosw
        lea     ax,ausMono2Color                  ;  Set address of color munge
        stosw
        mov     bx,ax                             ;  Save address
        mov     al,I_SS_OVERRIDE                  ;ss:
        stosb
        mov     ax,I_XOR_AL_MEM                   ;xor al,[xxxx]
        stosw
        lea     ax,[bx][1]                        ;  Set address of XOR mask
        stosw
already_phase_aligned_load_ds:
        mov     ax,cs                             ;Restore DS = CS
        mov     ds,ax
        assumes ds,Code
        jmp     short already_phase_aligned


cblt_3160:
        mov     ax,cs                             ;Restore DS = CS
        mov     ds,ax
        assumes ds,Code
        jmp     short cblt_phase_align


cblt_no_color_conversion:

;/*
;** Just need to generate the normal fetch sequence (lodsb)
;*/

        mov     al,I_LODSB                        ;Generate source fetch
        stosb

cblt_phase_align:
        call    phase_align_generate
already_phase_aligned:
        test    fbFetch,FF_TWO_INIT_FETCHES       ; Generate another fetch?
        jz      cblt_logical_action               ; No

;/*
;** A second fetch needs to be stuffed.  Copy the one just created.
;*/

        mov     si,di                             ;Get start of fetch logic
        xchg    si,npFetchStart                   ;Set new start, get old
        mov     cx,di                             ;Compute how long fetch is
        sub     cx,si                             ;  and move the bytes


        CPUMode 386

        mov     ax,es
        mov     ds,ax
        mov     ax,cx
        shr     cx,2
        rep     movsd
        mov     cx,ax
        and     cx,3
        rep     movsb
        mov     ax,cs                             ;Must leave DS = CS
        mov     ds,ax

        CPUMode 286

   


        subttl  Compile - ROP Generation
        page

;/*
;**       Create the logic action code
;**
;**       The given ROP will be converted into the actual code that
;**       performs the ROP.
;/*


SRC_IN_AL       equ     00000001b                ; Source field is in AL  (0)
DEST_IN_AH      equ     00000010b                ; Destination field is in AH (1)
PUSH_POP_FLAG   equ     00000100b                ; Next push/pop is a pop (1)


;/*
;**       Copy the ROP template into the BLT
;*/

cblt_logical_action:

;/*
;** The special gray rop will erroneously lead to Color Pat Fetch. 
;** It should be mono fetch of the transparency mask.   
;*/
                                                      
        test    fsBlt,BBF_GRAY_ROP                   
        jz      cblt_not_special_gray_rop           
        mov     si,BitBltOFFSET gray_rop_template  

        CPUMode 386
if      LENGTH_GRAY_ROP_TEMPLATE GE 8
        mov     cx,LENGTH_GRAY_ROP_TEMPLATE/4
        rep     movsd
else
if      LENGTH_GRAY_ROP_TEMPLATE AND 4
        movsd
endif
endif
if      LENGTH_GRAY_ROP_TEMPLATE AND 2
        movsw
endif
if      LENGTH_GRAY_ROP_TEMPLATE AND 1
        movsb
endif
        CPUMode 286
        jmp     short   cblt_srccopy                

cblt_not_special_gray_rop:                            

        mov     ax,usMixData                      ; Get back rop data
        mov     bl,ah                             ; Get count of number of bits to move
        and     bx,HIGH ROPLength
        shr     bx,1
        shr     bx,1
        mov     cl,byte ptr ropcode[bx] ; Get length into cx
        xor     ch,ch                             ; 
        .errnz   ROPLength - 0001110000000000b

        mov     bx,ax                             ; Get offset of the template
        and     bx,ROPOffset
        jz      cblt_srccopy                      ; Source copy
        lea     si,ropcode[bx]                          ; --> the template
cblt_copy_template:
        rep     movsb                             ; Move the template
cblt_srccopy:

        mov     bx,ax                             ; Keep rop around. !!!never used (I think)
        or      ah,ah                             ; Generate a negate?
   
        jns     cblt_no_NOT                       ;  No
        mov     ax,I_NOT_AL
        stosw
cblt_no_NOT:

;/*
;** Add transparency mask if needed:                   
;*/
                                                     
        test    fsBlt,BBF_TRANS or BBF_ANTI_TRANS   
        jz      cblt_transparency_not_needed        
        mov     si,BitBltOFFSET transparency_template   

        CPUMode 386
if      LENGTH_TRANSPARENCY_TEMPLATE GE 8
        mov     cx,LENGTH_TRANSPARENCY_TEMPLATE/4
        rep     movsd
else
if      LENGTH_TRANSPARENCY_TEMPLATE AND 4
        movsd
endif
endif
if      LENGTH_TRANSPARENCY_TEMPLATE AND 2
        movsw
endif
if      LENGTH_TRANSPARENCY_TEMPLATE AND 1
        movsb
endif
        CPUMode 286

cblt_transparency_not_needed:                         

        mov     npFetchEnd,di                     ; Save end of fetch/logic operation



        subttl  Compile - Mask And Save
        page

;/*
;**       Generate code to mask and save the result.  If the destination
;**       isn't in a register, it will be loaded from ES:[DI] first.  The
;**       mask operation will then be performed, and the result stored.
;*/

        mov     al,I_ES_OVERRIDE                 ;Load destination in AH
        stosb
        mov     ax,I_MOV_AH_DEST
        stosw

cblt_4280:

        CPUMode 386
        mov     si,BitBltOFFSET masked_store     ;Move rest of masked store template
        movsd
        movsw
        .errnz  MASKED_STORE_LEN - 6             ; Must be six bytes long
        CPUMode 286

        mov     ax,mStart                        ; Stuff start mask into
        xchg    ah,al                            ;   the template
        mov     es:MASKED_STORE_MASK[di],ax
        mov     npFetchEnds,di                   ; Save end of fetch/logic/store operation

        assumes ds,nothing



        subttl  Compile - Inner Loop Generation
        page

;/*
;**       Now for the hard stuff; The inner loop (said with a "gasp!").
;**
;**       If there is no innerloop, then no code will be generated
;**       (now that's fast!).
;/*

cblt_5000:
        mov     ax,es                             ; Set ds: to es: since code will be
        mov     ds,ax                             ;   copied from/to the stack
        assumes ds,nothing
        mov     dx,cInnerByte                     ; Get the loop count
        or      dx,dx                             ; If the count is null
        njz     cblt_inner_loop_done              ;  don't generate any code.


;/*
;**     We have something for a loop count.  If this just happens to be
;**     a source copy (S) with a phase of zero, then the innerloop degenerates
;**     to a repeated MOVSB instruction.  This little special case is
;**     worth checking for and handling!
;**
;**     Also, if this is one of the special cases {P, Pn, DDx, DDxn}, then it
;**     will also be special cased since these are all pattern fills (pattern,
;**     not pattern, 0, 1).
;**
;**     The same code can be shared for these routines, with the exception
;**     that patterns use a STOSx instruction instead of a MOVSx instruction
;**     and need a value loaded in AX
;**
;**
;**     So we lied a little.  If a color conversion is going on, then the
;**     REP MOVSB might not be usable.           If the F1_REP_OK flag has been set, then
;**     we can use it.  The F1_REP_OK flag will be set for a mono ==> color
;**     conversion where the background color is white and the foreground
;**     color is black, or for a color ==> mono conversion with the screen
;**     as the source (the color compare register will be used).
;**
;**     For the special cases {P, Pn, DDx, DDxn}, color conversion is
;**     not possible, so ignore it for them.
;*/

        mov     al,byte ptr usMix[0]             ; Get the raster op
        cmp     al,BB_ROP_S
        je      cblt_rop_s
        mov     bx,I_MOV_AL_0FFH                 ; Assume this is a 0 or 1 fill
        cmp     al,BB_ROP_DDxn
        je      cblt_rop_p
        cmp     al,BB_ROP_DDx
        je      cblt_rop_p
        mov     bx,I_MOV_AL_DH                   ; Assume P or Pn fill
        cmp     al,BB_ROP_P
        je      cblt_rop_p
        cmp     al,BB_ROP_Pn
        njne    cblt_cannot_rep                  ; Cannot special case it
cblt_rop_p:

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;
;       We should have one of the following fill operations:
;
;               P       - Pattern
;               Pn      - NOT pattern
;               DDx     - 0 fill
;               DDxn    - 1 fill
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;

        xchg    ax,bx
        stosw
        mov     ax,I_MOV_AH_AL          ; The fill in AX = AL:AL
        stosw
        mov     si,I_STOSB                        ; Set up for repeated code processor
        cmp     bl,BB_ROP_P                       ; If Pn or 0, then complement pattern
        je      cblt_check_length
        cmp     bl,BB_ROP_DDxn
        je      cblt_check_length
        mov     ax,I_NOT_AX                       ; Is Pn or 0, complement AX
        stosw

;/*
;**       Now, we have the fill in AX; we still need to duplicate it to
;**       make EAX = AX:AX if necessary.
;*/

cblt_check_length:
        cmp     dx,4                              ; If we going to use STOSD
        jb      cblt_5080                         ; we need the fill in EAX

        CPUMode 386

        mov     bx,I_MOV_AH_AL                    ; The fill in AX = AL:AL
        mov     eax, (08h shl 24) + (I_ROR_AX_N shl 8) + I_USE_386
        stosd                           ; ROR EAX,8 => EAX = AL:X:X:AL
        xchg    eax,ebx
        stosw                           ; MOV AH,AL => EAX = AL:X:AL:AL
        xchg    eax,ebx
        stosd                           ; ROR EAX,8 => EAX = AL:AL:X:AL
        xchg    eax,ebx
        stosw                           ; MOV AH,AL => EAX = AL:AL:AL:AL

        CPUMode 286

        jmp     short cblt_5080

cblt_rop_s:

;/*
;**       This is a source copy.                   The phase must be zero for a source copy
;**       to be condensed into a REP MOVSx.
;*/

        cmp     iHorzPhase,0                      ; Is horizontal phase zero?
        njne    cblt_cannot_rep                   ;  No, can't condense source copy
        mov     si,I_MOVSB                        ; Set register for moving bytes

;/*
;**       For a color conversion, F1_REP_OK must be set.
;*/

        test    fsBlt,BBF_TRANS or BBF_ANTI_TRANS
        njnz    cblt_cannot_rep
        test    fbF0,F0_GAG_CHOKE                ; Color conversion?
        jz      cblt_can_use_rep                 ;  No, rep is OK to use
        test    fbMore,F1_REP_OK                 ;  Yes, can we rep it?
        njz     cblt_cannot_rep                  ;    No, do it the hard way

cblt_can_use_rep:

;/*
;**     This is a source copy or pattern fill.   If there aren't many
;**     bytes to process then a REP MOVSD or REP STOSD is used.  MOVSB's
;**     or STOSB's are then used to catch any bytes not processed when
;**     the number of bytes is not a multiple of 4.  If the REP prefix
;**     isn't needed it is left out.
;**
;**     If many bytes are being processed then MOVSB's are used to
;**     align SI on a DWORD boundary before the use of the REP MOVSD.
;**     This should save clock cycles.
;**
;**     Don't get caught on this like I did!  If the direction of the
;**     BLT is from right to left (decrementing addresses), then both
;**     the source and destination pointers must be decremented by three
;**     so that the next four bytes are processed, not the next byte and
;**     the 3 bytes just processed.  Also, after all dwords have been processed,
;**     the source and destination pointers must be incremented by three to
;**     point to the last byte (since the last MOVSD or STOSD would have
;**     decremented both pointers by 4).
;**
;**     The generated code should look something like:
;**
;**     WARP8:                                    ;This code for moving left to right
;**             movsb                             ;Dword alignment
;**             ld      cx,cInnerByte/4 ;Set word count
;**             rep                               ;If a count, then repeat is needed
;**             movsd                             ;Move words until done
;**             movsb                             ;Process odd bytes
;**
;**
;**     WARP8:                                    ;This code for moving right to left
;**             movsb                             ;Dword alignment
;**             dec     si                        ;adjust pointer for moving words
;**             dec     di
;**             dec     si                        ;adjust pointer for moving words
;**             dec     di
;**             dec     si                        ;adjust pointer for moving words
;**             dec     di
;**             ld      cx,cInnerByte/4 ;Set word count
;**             rep                               ;If a count, then repeat is needed
;**             movsd                             ;Move words until done
;**             inc     si                        ;adjust since words were moved
;**             inc     di
;**             inc     si                        ;adjust since words were moved
;**             inc     di
;**             inc     si                        ;adjust since words were moved
;**             inc     di
;**             movsb                             ;Process odd byte
;**
;**
;**     Of course, if any part of the above routine isn't needed, it isn't
;**     generated (i.e. the generated code might just be a single MOVSB)
;*/

;/*
;** The following piece of code is used to speed up the source blt for the
;** width of more than 4 bytes by using MOVSD. However, if we want to
;** fully utilize its speed, we have to start fetching from the DWORD
;** boundary.
;/*

        .386P

        cmp     dx,TOO_SMALL_TO_ALIGN
        jb      cblt_5080
        mov     ax,si                   ;We want to start MOVSD at a DWORD
        mov     ah,al                   ;boundary, so we may do some prefetch

cblt_dword_alignment:
        mov     cx,devSrc.lp_bits.lo    ;Get the source's starting point
        mov     bl,iStepDir             ;See which direction we are going
        .errnz  STEPLEFT                ;Get the right increment in BX, i.e.
        .errnz  STEPRIGHT-1             ;   BX = +1 for left-to-right
        shl     bl,1                    ;   BX = -1 for right-to-left
        dec     bl
        add     cl,bl                   ;The first byte is alredy fetched
        test    fbFetch,FF_TWO_INIT_FETCHES
        jz      @F
        add     cl,bl                   ;Yes, update to the current point
@@:
        or      bl,bl                   ;Get the offset from the next DWORD
        js      @F                      ;boundary!!
        not     cl                      ;For StepRight, the offset equals
@@:                                     ;For StepLeft, the offset equals
        inc     cl                      ;(current position + 1) mod 4
        shr     cl,1
        jnc     @F                      ;If there is an odd byte,
        stosb                           ;fetch it first
        dec     dx
@@:
        shr     cl,1
        jnc     cblt_5080               ;If there is an odd word,
        stosw                           ;fetch it once more
        dec     dx
        dec     dx

cblt_5080:
        mov     ax,si
        mov     cx,dx                   ;Preserve it for future use
        shr     cx,2                    ;Check the number of DWORD to blt
 
cblt_5090:                              ; This is being used as a double jmp pt for jz's
        jz      cblt_5130               ;No more bytes to move
        xor     bx,bx                   ;Flag as stepping from left to right
        cmp     bl,iStepDir             ;Moving from the right to the left?
        errnz   STEPLEFT                ;  (left direction must be zero)
        jnz     cblt_5100               ;  No
        mov     eax,I_DEC_SI_DEC_DI + (I_DEC_SI_DEC_DI shl 16)
        stosw                           ;  Yes, decrement both pointers
        stosd
        mov     ebx,I_INC_SI_INC_DI  + (I_INC_SI_INC_DI shl 16)
                                        ;Set up to increment the pointers later

cblt_5100:
        cmp     cx,1                             ;Move one word or many words?
        jz      cblt_5120                        ;  Only one word
        mov     al,I_MOV_CX_WORD_I               ;  Many words, load count
        mov     ah,cl
        stosw
        mov     al,ch                            ;Set MSB of count
        mov     ah,I_REP                         ;  and a repeat instruction
        stosw

cblt_5120:
        mov     ax,si                            ;Set the word instruction
        inc     ax
        mov     ah,I_USE_386                     ;Prefix the 16-bit data transfer instr.
        xchg    ah,al                            ;with 66 to make it become 32-bit
        stosw

        .errnz   I_MOVSW - I_MOVSB - 1           ; The word form of the instruction
        .errnz   I_STOSW - I_STOSB - 1           ;   must be the byte form + 1

        or      bx,bx                            ; Need to increment the pointers?
        jz      cblt_5130                        ;   No
        mov     eax,ebx                          ;   Yes, increment both pointers
        stosd
        stosw

cblt_5130:
        mov     ax,si
        shr     dx,1                              ;Byte count / 2 for words
        jnc     @F                                ;  No odd byte to move
        stosb

@@:                                               ; This is being used as a double jmp pt for jz's
        shr     dx,1                              ;move another two odd bytes if needed
        jnc     cblt_5140
        mov     ah,al                             ;We use two 8-bit transfer because we
        stosw                                     ;don't want to adjust SI and DI

        .286P

cblt_no_inner_loop_code:
cblt_5140:
        jmp     cblt_inner_loop_done              ; Done setting up the innerloop
        page

;/*
;**      There is some count for the innerloop of the BLT.  Generate the
;**       required BLT. Two or four copies of the BLT will be placed on the
;**       stack.   This allows the LOOP instruction at the end to be distributed
;**       over two or four bytes instead of 1, saving 11 or 12 clocks for each
;**       byte (for 4).  Multiply 12 clocks by ~ 16K and you save a lot of
;**       clocks!
;**
;**       If there are less than four (two) bytes to be BLTed, then no looping
;**       instructions will be generated.  If there are more than four (two)
;**       bytes, then there is the possibility of an initial jump instruction
;**       to enter the loop to handle the modulo n result of the loop count.
;**
;**       The innerloop code will look something like:
;**
;**
;**       <       mov     cx,loopcount/n> ;load count if >n innerloop bytes
;**       <       jmp     short ???     > ;If a first jump is needed, do one
;**
;**       BLTloop:
;**               replicate initial byte BLT code up to n times
;**
;**       <       loop    BLTloop >                ;Loop until all bytes processed
;*/

cblt_cannot_rep:
        mov     bx,npFetchEnd                     ;Compute size of the fetch code
        sub     bx,npFetchStart
        inc     bx                                ;A stosb will be appended
        mov     si,4                              ;Assume replication 4 times
        mov     cl,2                              ;  (shift count two bits left)
        cmp     bx,32                             ;Small enough for 4 times?
        jc      cblt_5520                         ;  Yes, replicate 4 times
        shr     si,1                              ;  No, replicate 2 times
        dec     cx

cblt_5520:
        cmp     dx,si                             ;Generate a loop?
        jle     cblt_5540                         ;  No, just copy code
        mov     al,I_MOV_CX_WORD_I
        stosb                                     ;mov cx,loopcount/n
        mov     ax,dx                             ;Compute loop count
        shr     ax,cl
        stosw
        shl     ax,cl                             ;See if loopcount MOD n is 0
        sub     ax,dx
        jz      cblt_5540                         ;Zero, no odd count to handle

;/*
;**       There is an odd portion of bytes to be processed.  Increment
;**       the loop counter for the odd pass through the loop and then
;**       compute the displacement for entering the loop.
;**
;**       To compute the displacement, subtract the number of odd bytes
;**       from the modulus being used  (i.e. 4-3=1).  This gives the
;**       number of bytes to skip over the first time through the loop.
;**
;**       Multiply this by the number of bytes for a logic sequence,
;**       and the result will be the displacement for the jump.
;*/

        inc     word ptr es:-2[di]                ;Not zero, adjust for partial loop
        add     ax,si                             ;Compute where to enter the loop at
        mul     bl
        mov     cx,ax
        mov     al,I_JMP_NEAR                     ;Stuff jump instruction
        stosb
        mov     ax,cx                             ;Stuff displacement for jump
        stosw


;/*
;**     Currently:      DX = loop count
;**                     SI = loop modulus
;**                     BX = size of one logic operation
;**                     DI --> next location in the loop
;*/

cblt_5540:
        mov     cx,bx                             ;Set move count
        mov     bx,dx                             ;Set maximum for move
        cmp     bx,si                             ;Is the max > what's left?
        jle     cblt_5560                         ;  No, just use what's left
        mov     bx,si                             ;  Yes, copy the max

cblt_5560:

        CPUMode 386

        sub     dx,si                             ;If dx > 0, then loop logic needed
        mov     si,npFetchStart                   ;--> fetch code to copy
        mov     ax,cx                             ;Save a copy of fetch length
        shr     cx,2
        rep     movsd                             ;Move fetch code and stuff stosb
        mov     cx,ax
        and     cx,3
        rep     movsb
        mov     si,di                             ;--> new source (and top of loop)
        sub     si,ax
        mov     byte ptr es:-1[di],I_STOSB
        dec     bl                                ;One copy has been made
        mul     bl                                ;Compute # bytes left to move
        mov     cx,ax                             ;Set move count
        shr     cx,2
        rep     movsd                             ;Move fetch code and stuff stosb
        mov     cx,ax
        and     cx,3
        rep     movsb
        sub     si,ax                             ;Restore pointer to start of loop

        CPUMode 286


;/*
;**        The innermost BLT code has been created and needs the looping
;**        logic added to it.  If there is any looping to be done, then
;**        generate the loop code.  The code within the innerloop may be
;**        greater than 126 bytes, so a LOOP instruction may not be used
;**        in this case.
;/*

cblt_5580:
        or      dx,dx                                ; Need a loop?
        jle     cblt_inner_loop_done                 ;   No, don't generate one

        mov     ax,si                                ; Compute offset of loop
        sub     ax,di
        cmp     ax,-125                              ; Can this be a short label?
        jc      cblt_5600                            ;   No, must make it a near jmp

        sub     al,2                                 ; Bias offset by length of LOOP inst.
        mov     ah,al
        mov     al,I_LOOP
        stosw                                        ; Set the loop instruction
        jmp     short cblt_inner_loop_done           ; Go process the last byte code


cblt_5600:

        CPUMode 386
        mov     si,BitBltOFFSET jmp_cx_nz             ;Move in the dec CX jnz code
        movs    dword ptr es:[di],dword ptr cs:[si]
        errnz   JMP_CX_NZ_LEN-4                       ;Must be four bytes long
        sub     ax,6                                  ;Adjust jump bias
        stosw                                         ;  and store it into jump
        CPUMode 286



        subttl  Compile - Last Byte Processing
        page

cblt_inner_loop_done:

;/*
;**     All the innerloop stuff has been processed.  Now generate the code for
;**     the final byte if there is one.  This code is almost identical to the
;**     code for the first byte except there will only be one fetch (if a
;**     fetch is needed at all).
;**
;**     The code generated will look something like:
;**
;**     <       fetch                            > ; Get source byte
;**     <       align                            > ; Align source if needed
;**             action                            ; Perform desired action
;**             mask and store
;*/

        mov     dx,mLast                               ; Get last byte mask
        or      dh,dh                                  ; Is there a last byte to be processed?
        jz      cblt_no_last_byte                      ;   No.

        mov     cx,npFetchEnds                         ; Get end of fetch/logic/store operation
        mov     si,npFetchStart                        ; Get start of fetch/logic sequence
        sub     cx,si                                  ; Compute length of the code
        test    fbFetch,FF_NO_LAST_FETCH
        jz      cblt_include_fetch
        test    fbF0,F0_SRC_PRESENT                    ; was there a fetch?
        jz      cblt_was_no_fetch
        cmp     iHorzPhase,0                           ; Phase zero case is not combined
                                                       ; into innerloop as it should be.
                                                       ; If the final byte is full then we
                                                       ; better not remove the lodsb ( i.e.
        je      cblt_include_fetch                     ; 0 - 0 = 0 would make us think we could)

        mov     ax,cFetchCode                          ; don't copy the fetch (lodsb)
        add     si,ax
        sub     cx,ax

cblt_was_no_fetch:
cblt_include_fetch:

        CPUMode 386

        mov     ax,cx
        shr     cx,2
        rep     movsd                 ; Copy the fetch/action/store code
        mov     cx,ax
        and     cx,3
        rep     movsb

        CPUMode 286

        xchg    dh,dl
        mov     MASKED_STORE_MASK[di],dx  ; Stuff last byte mask into the code


        subttl  Compile - Looping Logic
        page

;/*
;**     Looping logic.
;**
;**     The looping logic must handle monochrome bitmaps, color bitmaps,
;**     huge bitmaps, the device, the presence or absence of a source
;**     or pattern, and mono <==> color interactions.
;**
;**     The type of looping logic is always based on the destination.
;**
;**
;**     Plane Update Facts:
;**
;**     1)  If the destination device is color, then there will be
;**         logic for plane selection.           Plane selection is performed
;**         at the start of the loop for the display.  Plane selection
;**         for bitmaps is performed at the end of the loop in anticipation
;**         of the next plane.
;**
;**
;**         The following applies when the destination is color:
;**
;**
;**         a)  The destination update consists of:
;**
;**             1)  If the destination is the display, the next plane will
;**                 be selected by the plane selection code at the start
;**                 of the scan line loop.
;**
;**             2)  If not the display, then the PDevice must a bitmap.
;**                 The next plane will be selected by updating the
;**                 destination offset by the next_plane value.
;**
;**
;**         b)  If F0_GAG_CHOKE isn't specified, then there may be a source.
;**             If there is a source, it must be color, and the update
;**             consists of:
;**
;**             1)  If the source is the display, the next plane will be
;**                 selected by the plane selection code at the start of
;**                 the loop.
;**
;**             2)  If not the display, then the PDevice must a bitmap.
;**                 The next plane will be selected by updating the
;**                 --source--- offset by the next_plane value.
;**
;**
;**         c)  If F0_GAG_CHOKE is specified, then the source must be a
;**             monochrome bitmap which is undergoing mono to color
;**             conversion.  The AND & XOR mask table which is used
;**             for the conversion will have to be updated, unless
;**             the F1_NO_MUNGE flag is set indicating that the color
;**             conversion really wasn't needed.
;**
;**             The source's pointer will not be updated.  It will
;**             remain pointing to the same scan of the source until
;**             all planes of the destination have been processed.
;**
;**
;**         d)  In all cases, the plane mask rotation code will be
;**             generated.  If the plane indicator doesn't overflow,
;**             then start at the top of the scan line loop for the
;**             next plane.
;**
;**             If the plane indicator overflows, then:
;**
;**                 1)  If there is a pattern present, it's a color
;**                     pattern fetch.           The index of which scan of
;**                     the brush to use will have to be updated.
;**
;**                 2)  Enter the scan line update routine
;**
;**
;**     2)      If the destination is monochrome, then there will be no
;**             plane selection logic.
;**
;**             If F0_GAG_CHOKE is specified, then color ==> mono conversion
;**             is taking place.  Any plane selection logic is internal
;**             to the ROP byte fetch code.  Any color brush was pre-
;**             processed into a monochrome brush, so no brush updating
;**             need be done
;*/



        subttl  Looping Logic - Plane Selection
        page

;/*
;**     Get saved parameters off of the stack.
;**
;**     <       pop     bx                             > ;Get plane indicator
;**     <       pop     si                             > ;Get source pointer
;**             pop     di                        ;Get destination pointer
;**             pop     cx                        ;Get loop count
;*/


cblt_no_last_byte:
        mov     ax,cs                             ;Reset ds: back to cs:
        mov     ds,ax
        mov     bh,fbF0                           ;These flags will be used a lot
        test    bh,F0_DEST_IS_COLOR               ;Is the destination color?
        jz      cblt_6120                         ;  No
        mov     al,I_POP_BX                       ;Restore plane index
        stosb

cblt_6120:
        test    bh,F0_SRC_PRESENT                 ;Is a source needed?
        jz      cblt_6140                         ;  No
        mov     al,I_POP_SI                       ;  Yes, get source pointer
        stosb

cblt_6140:
        mov     ax,I_POP_DI_POP_CX                ;Get destination pointer
        stosw                                     ;Get loop count
        test    bh,F0_DEST_IS_COLOR               ;Color scanline update?
        jnz     cblt_6160                         ;  Yes
        jmp     cblt_6300                         ;  No, just do the mono scanline update




;/*
;**       The scanline update is for color.  Generate the logic to update
;**       a brush, perform plane selection, process mono ==> color conversion,
;**       and test for plane overflow.
;*/


cblt_6160:
        or      bh,bh                             ;Color conversion?
        jns     cblt_6180                         ;  No
        errnz   F0_GAG_CHOKE-10000000b

;/*
;**     The source is monochrome.  Handle mono ==> color conversion.
;**     The AND & XOR mask table will need to be rotated for the next
;**     pass over the source.
;**
;**     The source scanline pointer will not be updated until all planes
;**     have been processed for the current scan.
;**
;**     If F1_NO_MUNGE has been specified, then the color conversion table
;**     and the color conversion code was not generated, and no update
;**     code will be needed.
;*/


        test    fbMore,F1_NO_MUNGE               ;Is there really a conversion table?
        jnz     short cblt_6200                  ;  No, so skip the code

        mov     al,I_MOV_BP_WORD_I               ;lea bp,cl_a_brush
        stosb
        lea     ax,ausMono2Color                 ;Get address of table
        stosw
        lea     si,rot_and_xor                   ;--> rotate code

        CPUMode 386
if      LEN_ROT_AND_XOR GE 8
        mov     cx,LEN_ROT_AND_XOR/4
        rep     movsd
else
if      LEN_ROT_AND_XOR AND 4
        movsd
endif
endif
if      LEN_ROT_AND_XOR AND 2
        movsw
endif
if      LEN_ROT_AND_XOR AND 1
        movsb
endif
        CPUMode 286

        jmp     short cblt_6200



;/*
;**       If there is a source, it must be color.  If it is a memory
;**       bitmap, then the next plane must be selected, else it is
;**       the display and the next plane will be selected through
;**       the hardware registers.
;**
;**
;**       <       add     si,next_plane>
;*/


cblt_6180:
        test    bh,F0_SRC_PRESENT                 ;Is there really a source?
        jz      cblt_6200                         ;No source.
        test    bh,F0_SRC_IS_DEV                  ;Is the source the display?
        jnz     cblt_6200                         ;  Yes, use hardware plane selection
        mov     ax,I_ADD_SI_WORD_I                ;  No, generate plane update
        stosw                                     ;Add si,next_plane
        mov     ax,devSrc.next_plane
        stosw

;/*
;**     If the destination isn't the device, then it must be a color
;**     memory bitamp, and it's pointer will have to be updated by
;**     bmWidthPlanes.  If it is the display, then the next plane
;**     will be selected through the hardware registers.
;**
;**     <       add     di,next_plane>
;*/

cblt_6200:
        test    bh,F0_DEST_IS_DEV                ;Is the destination the display
        jnz     cblt_6220                        ;  Yes, don't generate update code
        mov     ax,I_ADD_DI_WORD_I               ;  No, update bitmap to the next plane
        stosw
        mov     ax,devDst.next_plane
        stosw





;/*
;**     The source and destination pointers have been updated.
;**     Now generate the plane looping logic.
;**
;**     <       shl     bl,1                           > ;Select next plane
;**     <       jc      $+5                            > ;  No, reset to first
;**     <       jmp     StartOfLoop   > ;  Yes, go process next
;**     <       mov     bl,PLANE_1     > ;Reset plane indicator
;**
;**     or
;**
;**     <       shl     bl,1                           > ;Select next plane
;**     <       jnc     StartOfLoop   > ;  Yes, go process next
;**     <       mov     bl,PLANE_1     > ;Reset plane indicator
;*/

cblt_6220:
        mov     ax,I_SHL_BL_1                     ;Stuff plane looping logic
        stosw

        mov     dx,pfnBlt.off                     ;Compute relative offset of
        sub     dx,di                             ;  start of loop
        add     dx,2                              ;account for init of plane indicator
        cmp     dx,-125                           ;Can this be a short label?
        jc      cblt_6240                         ;  No, must make it a near jmp
        sub     dl,2                              ;Bias offset by length of jz inst.
        mov     ah,dl
        mov     al,I_JNC
        stosw                                     ;jnc StartOfLoop
        jmp     short cblt_6260

cblt_6240:
        mov     ax,I_JC_P5H                       ;jc $+5
        stosw
        mov     al,I_JMP_NEAR                     ;jmp near
        stosb
        sub     dx,5                              ;Adjust jump bias
        mov     ax,dx
        stosw                                     ;Store jmp displacement

cblt_6260:
        mov     ax,(PLANE_SELECT_FLAG shl 8)+I_MOV_BL_BYTE_I
        stosw



        subttl  Looping Logic - Color Brush Update
        page

;/*
;**  The plane update logic has been copied.  If a pattern was
;**  involved for a color BLT, then the pattern index will need
;**  to be updated to the next scanline for multiple plane mode.
;** 
;**  This will involve subtracting off NUMBER_PLANES*SIZE_PATTERN (MonoPlane),
;**  and adding in the increment.  The result must be masked with
;**  00000111b to select the correct source.  Note that the update
;**  can be done with an add instruction and a mask operation.
;** 
;**        inc   index+MonoPlane                    inc-MonoPlane result  AND 07h
;** 
;**         1       0+24 = 24                         1-24 = -23    1      1
;**         1       7+24 = 31                         1-24 = -23    8      0
;**        -1       0+24 = 24                        -1-24 = -25   FF      7
;**        -1       7+24 = 31                        -1-24 = -25    6      6
;** 
;**        <       mov     al,ss:[1234]  > ;Get brush index
;**        <       add     al,n                           > ;Add displacement to next byte
;**        <       and     al,00000111b  > ;Keep it in range
;**        <       mov     ss:[1234],al  > ;Store displacement to next byte
;** 
;** 
;**  For four plane mode, the AND 00011111b automatically wraps the
;**  pattern at the correct location, so no code is generated.
;*/

        test    bh,F0_PAT_PRESENT                 ;Is a pattern involved?
        jz      cblt_6300                         ;  No
        mov     ax,I_SS_OVERRIDE+(I_MOV_AL_MEM*256)
        stosw                                     ;mov al,ss:[xxxx]
        mov     dx,npbPatRow
        mov     ax,dx
        stosw
        mov     al,I_ADD_AL_BYTE_I
        mov     ah,iDir                           ;add al,bias
        sub     ah,BITS_PEL * SIZE_PATTERN
        errnz   INCREASE-1                        ;Must be a 1
        errnz   DECREASE+1                        ;Must be a -1
        stosw

        CPUMode 386

        mov     eax,((I_SS_OVERRIDE+(I_MOV_MEM_AL*256)) shl 16)+0700h+I_AND_AL_BYTE_I
        stosd                           
                                        

        CPUMode 286

        mov     ax,dx
        stosw

        subttl  Looping Logic - Scan Line Update
        page

;/*
;**     Generate the next scanline code.  The next scan line code must
;**     handle monochrome bitmaps, the device, huge bitmaps, the presence
;**     or absence of a source.
;**
;**     Also color bitmaps, and mono <==> color interactions.
;**
;**     <       add si,devSrc.next_scan> ;Normal source scan line update
;**     <       Huge Bitmap Update     > ;>64K source update code
;**             add di,devDst.next_scan ;Normal destination scan line update
;**     <       Huge Bitmap Update     > ;>64K destination update code
;**
;**
;**     All updates will at least consist of the add IndexReg,next_plane.
;*/


cblt_6300:
        mov     ch,iDir                           ;Load this for YUpdate code
        test    bh,F0_SRC_PRESENT                 ;Is there a source?
        jz      cblt_6340                         ;  No, skip source processing
        mov     dx,I_ADD_SI_WORD_I                ;add si,increment
        mov     bx,((HIGH I_MOV_SI_AX)*256)+(HIGH I_LEA_AX_SI_DISP16)
        mov     cl,HIGH I_MOV_AX_DS
        push    bp
        lea     bp,devSrc
        call    y_update                          ;Generate the Y scan line update code
        pop     bp                                ;Restore frame pointer

cblt_6340:
        mov     dx,I_ADD_DI_WORD_I                ;add reg,increment
        mov     bx,((HIGH I_MOV_DI_AX)*256)+(HIGH I_LEA_AX_DI_DISP16)
        mov     cl,HIGH I_MOV_AX_ES
        push    bp
        lea     bp,devDst                         ;--> destination data
        call    y_update                          ;Generate the Y scan line update code
        pop     bp                                ;Restore frame pointer



;/*
;**       Compile the scan line loop.  The code simply jumps to the start
;**       of the outer loop if more scans exist to be processed.
;*/


cblt_6380:
        mov     ax,pfnBlt.off                     ;Compute relative offset of
        sub     ax,di                             ;  start of loop
        add     dx,2                              ;account for init of plane indicator
        cmp     ax,-125                           ;Can this be a short label?
        jc      cblt_6400                         ;  No, must make it a near jmp
        sub     al,2                              ;Bias offset by length of LOOP inst.
        mov     ah,al
        mov     al,I_LOOP
        stosw                                     ;Set the loop instruction
        jmp     short cblt_6420

cblt_6400:

        CPUMode 386
        mov     si,BitBltOFFSET jmp_cx_nz         ;Move in the dec CX jnz code
        movsd
        errnz   JMP_CX_NZ_LEN-4                   ;Must be four bytes long
        sub     ax,6                              ;Adjust jump bias
        stosw                                     ;  and store it into jump
        CPUMode 286

cblt_6420:
        mov     al,I_RET_FAR                      ;Stuff the far return instruction
        stosb

;/*
;**------ ret --------
;*/
        db      I_RET_NEAR                        ;near return
cEnd    <nogen>

        subttl  Phase Align Code Generation
        page


;/***************************************************************************
;*
;* FUNCTION NAME = phase_align_generate 
;*
;* DESCRIPTION   = Generate the phase alignment if any.                     
;*                                                                          
;*                 It is assumed that AL contains the source byte  
;*
;*                 Registers Destroyed:  
;*                       AX,CX,SI        
;*
;* INPUT         = DH = phase alignment 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  phase_align_generate
phase_align_generate        proc    near

        assert  dh,E,iHorzPhase
        xor     cx,cx                             ;Might have garbage in it
        or      dh,dh                             ;Any phase alignment?
        jz      cblt_phase0                       ;  No, so skip alignment
        mov     cl,dh                             ;Get horizontal phase for rotating
        mov     ax,I_ROL_AL_N                     ;Assume rotate left n times
        cmp     cl,5                              ;4 or less rotates?
        jc      cblt_small_phase                  ;  Yes
        neg     cl                                ;  No, compute ROR count
        add     cl,8
        mov     ah,HIGH I_ROR_AL_N
        .errnz   (LOW I_ROL_AL_N) - (LOW I_ROR_AL_N)

cblt_small_phase:
        stosw                                     ;Stuff the phase alignment rotates
        mov     al,cl                             ;  then the phase alignment code
        stosb

;/*
;** Do not generate phase masking if there is only 1 src And only 1 dest byte.
;** This is not just an optimization, see comments where these flags are set.
;*/

        mov     al,fbFetch
        and     al,FF_ONLY_1_SRC_BYTE or FF_ONLY_1_DEST_BYTE
        xor     al,FF_ONLY_1_SRC_BYTE or FF_ONLY_1_DEST_BYTE
        jz      cblt_skip_masking

        mov     si,BitBltOFFSET phase_align

        .386P
if      PHASE_ALIGN_LEN GE 8
        mov     cx,(PHASE_ALIGN_LEN SHR 2)
        rep     movs    dword ptr es:[di],dword ptr cs:[si]
else
if      PHASE_ALIGN_LEN AND 4
        movs    dword ptr es:[di],dword ptr cs:[si]
endif
endif
if      PHASE_ALIGN_LEN AND 2
        movs    word ptr es:[di],word ptr cs:[si]
endif
if      PHASE_ALIGN_LEN AND 1
        movs    byte ptr es:[di],byte ptr cs:[si]
endif
        .286P

cblt_skip_masking:
cblt_phase0:
        ret
phase_align_generate        endp


        subttl  Scan Line Update Generation
        page


;/***************************************************************************
;*
;* FUNCTION NAME = y_update 
;*
;* DESCRIPTION   = 
;*                 
;*     Generate Y update code.
;*    
;*    
;*     The Y update code is generated as follows:
;*    
;*     For the display, small bitmaps, and huge bitmaps where the BLT
;*     doesn't span a segment bounday, all that need be done is add
;*     next_scan to the offset portion of the bits pointer. next_scan
;*     is a 2's complement if the BLT is Y-, so an addition can always
;*     be done.
;*    
;*         < add   si,next_scan >
;*           add   di,next_scan
;*    
;*    
;*     For huge bitmaps where the BLT spans a segment boundary, the
;*     above update must be performed, and the overflow/undeflow
;*     detected.  This isn't too hard to detect.
;*    
;*     For any huge bitmap, there can be a maximum of Planes*bmWidthBytes-1
;*     unused bytes in a 64K segment.  The minimum is 0.  The scan line
;*     update always updates to the first plane of the next (previous) scan.
;*    
;*    
;*     When the BLT is Y+, if the new offset is anywhere within the
;*     unused bytes of a segment, or in the first scan of a segment,
;*     then overflow must have occured:
;*    
;*           -bmFillBytes <= offset < Planes*bmWidthBytes
;*    
;*     Since the update is always made to the first plane of a scan,
;*     Planes in the above equation can be thrown out.  Also, if
;*     bmFillBytes is added to both sides of the equation:
;*    
;*           0 <= offset < bmWidthBytes+bmFillBytes   (unsigned compare)
;*    
;*     will be true if overflow occurs.  The Y+ overflow check will
;*     look like:
;*    
;*    
;*         lea ax,bmFillBytes[si]                      ;Adjust for fill bytes now
;*         cmp ax,bmWidthBytes+bmFillBytes             ;Overflow occur?
;*         jnc NoOverflow                              ;  No
;*         cmp cx,2                                    ;Any more scans?
;*         jnc NoOverflow                              ;  No, don't update selector
;*         add si,bmFillBytes                          ;Step over fill bytes
;*         mov ax,ds                                   ;Compute new selector
;*         add ax,bmSegmentIndex
;*         mov ds,ax
;*    
;*       NoOverflow:
;*    
;*    
;*    
;*     For Y- BLTs, the test is almost the same.  The equation becomes
;*    
;*        -(Planes*bmWidthBytes) > offset             (unsigned compare)
;*    
;*     then underflow occurs.  Planes in the above equation cannot be
;*     thrown out.  The Y- underflow check will look like:
;*    
;*         mov ax,si
;*         cmp ax,-(Planes*bmWidthBytes)               ;Overflow occur?
;*         jc  NoOverflow                              ;  No
;*         cmp cx,2                                    ;Any more scans?
;*         jnc NoOverflow                              ;  No, don't update selector
;*         add si,bmFillBytes                          ;Step over fill bytes
;*         mov ax,ds                                   ;Compute new selector
;*         add ax,bmSegmentIndex
;*         mov ds,ax
;*    
;*     bmFillBytes and bmSegment index will be the 2's complement by
;*     now if the BLT is Y-.
;*
;*                  Registers Preserved: 
;*                        DX,SI          
;*                  Registers Destroyed: 
;*                        AX,DI,flags    
;*
;* INPUT         = SS:BP --> source or destination data                 
;*                 SS:DI --> where to generate the code                 
;*                 DX     =  update register (add si,wordI & mov ax,si) 
;*                 BL     =  lea register (SI or DI)                    
;*                 BH     =  mov si,ax   or   mov di,ax register        
;*                 CL     =  segment register (DS or ES)                
;*                 CH     =  Direction                                  
;*
;* OUTPUT        = SS:BP --> source or destination data         
;*                 SS:DI --> where to generate the code         
;*                 BL     =  lea register (SI or DI)            
;*                 BH     =  mov si,ax   or   mov di,ax register
;*                 CL     =  segment register (DS or ES)        
;*                 CH     =  Direction                          
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  y_update
y_update        proc    near

;/*
;**       Stuff the scan line increment for the source or destination
;**
;**       <   add     si,1234h                     > ;Update source
;**       <   add     di,9ABCh                     > ;Update destination
;/*


        mov     ax,[bp].next_scan                 ;Get the increment
        or      ax,ax                             ;If zero, don't generate the code
        jz      y_update_10
        xchg    ax,dx                             ;Set opcode
        stosw
        xchg    ax,dx                             ;Set increment
        stosw

y_update_10:
        test    [bp].dev_flags,DEV_SPANS_SEG
        jnz     spans_a_segment
        ret



;/*
;**       The BLT spans a segment.  The code to detect when the segment is
;**       crossed must be generated, as given above.
;*/


spans_a_segment:
        mov     ah,dh                             ;Set register for MOV
        errnz   <(HIGH I_ADD_SI_WORD_I) - (HIGH I_MOV_AX_SI)>
        errnz   <(HIGH I_ADD_DI_WORD_I) - (HIGH I_MOV_AX_DI)>

        mov     al,LOW I_MOV_AX_SI                ;Assume Y- BLT
        errnz   <(LOW I_ADD_SI_WORD_I) - (LOW I_ADD_DI_WORD_I)>

        cmp     ch,DECREASE                       ;Y- BLT?
        je      y_update_30                       ;  Yes

        mov     ah,bl                             ;lea reg, bmFillBytes
        mov     al,LOW I_LEA_AX_SI_DISP16
        errnz   <(LOW I_LEA_AX_SI_DISP16) - (LOW I_LEA_AX_DI_DISP16)>

        stosw
        mov     ax,fill_bytes[bp]

y_update_30:
        stosw

        mov     al,I_CMP_AX_WORD_I
        stosb
        mov     ax,comp_value[bp]
        stosw

        mov     al,comp_test[bp]
        mov     ah,HIGH I_JC_P12H
        stosw
        errnz   <(HIGH I_JC_P12H) - (HIGH I_JNC_P12H)>

        CPUMode 386

        mov     eax,((2+((LOW I_JC_P0DH)*256)) shl 16)+I_CMP_CX_2
        stosd

        CPUMode 286

        mov     al,(HIGH I_JC_P0DH)
        stosb
        errnz   <(LOW I_MOV_SI_AX)-(LOW I_MOV_DI_AX)>

        xchg    ax,dx                             ;Get add si, or add di,
        stosw
        mov     ax,fill_bytes[bp]
        stosw

        mov     al,LOW I_MOV_AX_DS
        mov     ah,cl
        stosw
        errnz   <(LOW I_MOV_AX_DS)-(LOW I_MOV_AX_ES)>

        mov     al,I_ADD_AX_WORD_I
        stosb

        mov     ax,seg_index[bp]
        stosw

        mov     al,LOW I_MOV_DS_AX               ;mov SegmentReg,ax
        mov     ah,cl
        stosw
        errnz   <(LOW I_MOV_DS_AX)-(LOW I_MOV_ES_AX)>
        errnz   <(HIGH I_MOV_DS_AX)-(HIGH I_MOV_AX_DS)>
        errnz   <(HIGH I_MOV_ES_AX)-(HIGH I_MOV_AX_ES)>

y_update_40:
        ret

y_update        endp

        include cblt.pub

sEnd    BitBlt
end
