File:  [WindowsNT SDKs] / ntddk / src / video / displays / vga / i386 / special.asm
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

                page    ,132
;---------------------------Module-Header------------------------------;
; Module Name: special.asm
;
; Copyright (c) 1992 Microsoft Corporation
;-----------------------------------------------------------------------;
        title   Special
        .386

ifndef  DOS_PLATFORM
        .model  small,c
else
ifdef   STD_CALL
        .model  small,c
else
        .model  small,pascal
endif;  STD_CALL
endif;  DOS_PLATFORM

        assume cs:FLAT,ds:FLAT,es:FLAT,ss:FLAT
        assume fs:nothing,gs:nothing

        .data

;BUGBUG this should be obtained from the surface

        extrn   ulNextScan_global:dword

        .code

_TEXT$01   SEGMENT DWORD USE32 PUBLIC 'CODE'
           ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

        .xlist
        include stdcall.inc             ;calling convention cmacros
        include i386\cmacFLAT.inc       ; FLATland cmacros
        include i386\display.inc        ; Display specific structures
        include i386\ppc.inc            ; Pack pel conversion structure
        include i386\bitblt.inc         ; General definitions
        include i386\ropdefs.inc        ; Rop definitions
        include i386\egavga.inc         ; EGA register definitions
        include i386\strucs.inc
        .list

; Rops we use in this code

ROP_P           equ     0F0h
ROP_Pn          equ      0Fh
ROP_S           equ     0CCh
ROP_DDx         equ       0
ROP_DDxn        equ     0FFh
ROP_Dn          equ     055h
ROP_DPx         equ     05Ah

; Other values used in this code

fr              equ     [ebp]           ; Access to bitblt frame
bptr            equ     byte ptr

;----------------------------Public Routine----------------------------;
; check_device_special_cases
;
; Check for fast special cases of BLT.
;
; Determine if the BLT is a special case which can be performed with
; static code as opposed to code compiled on the stack, and, if so,
; dispatch to the proper static code.
;
; The parameters needed for the BLT (phase alignment, directions of
; movement, ...) have been computed and saved.  These parameters will
; now be interpreted and a BLT created on the stack.
;
; If the raster op is source copy, both devices are the screen, and the
; phase alignment is 0, then the copy can be performed by the static
; code using the EGA's write mode 1.
;
; If the rasterop is P, Pn, DDx (0), DDxn (1), and the brush is solid
; or grey (for P and Pn), and the destination device is the screen,
; then the operation can be performed by the static code using the EGA's
; write mode 2 (write mode 0 for greys).
;
; Entry:
;       EDI --> pointer to target surface
;       EBP --> frame of BitBLT local variables
;       EGA registers in default state
; Returns:
;       Carry set if BLT was performed with static code.
; Error Returns:
;       Carry clear if BLT was not a special case.
; Registers Destroyed:
;       AX,BX,CX,DX,SI,DI,DS,ES,flags
; Registers Preserved:
;       EBP
; Calls:
;-----------------------------------------------------------------------;

cProc   check_device_special_cases

        xor     cx,cx                       ;This is used, what is it????!!!
        mov     dh,fr.the_flags             ;Keep the flags in DH for a while
        test    dh,F0_DEST_IS_DEV           ;Is the destination a device?
        jz      cdsc_blt_not_special_cased  ;Not device, cannot special case it
        mov     edi,fr.dest.next_scan       ;Special case code expects this
        mov     al,byte ptr fr.Rop[0]       ;Get the raster op
        cmp     al,ROP_S                    ;Is it src copy?
        je      cdsc_its_src_copy     ;  Yes, go check it out
        cmp     al,ROP_P
        je      short cdsc_its_patblt

cdsc_not_s_or_p:
        xor     bl,bl                       ;Color for 0 fill
        cmp     al,ROP_DDx
        je      short cdsc_its_1_or_0
        cmp     al,ROP_Pn
        je      short cdsc_its_inverse_patblt
        cmp     al,ROP_Dn
        je      short cdsc_its_dn
        cmp     al,ROP_DPx
        je      short cdsc_its_dpx
        cmp     al,ROP_DDxn
        jne     cdsc_blt_not_special_cased
        mov     bl,0FFh                     ;Color for 1 fill

; It's "1" (DDxn) or "0" (DDx)
cdsc_its_1_or_0:
        mov     fr.brush_accel,SOLID_BRUSH  ;(no brush given for DDx or DDxn)
        call    ega_solid_pat
        jmp     cdsc_exit

cdsc_its_patblt:
        mov     bl,fr.brush_accel       ;Ccolor in lower bits, flags in upper
        test    bl,SOLID_BRUSH
        jnz     short cdsc_its_a_solid_color
        test    bl,GREY_SCALE
        jz      short cdsc_not_solid_nor_grey
        xor     bl,bl                   ;It's grey.  !!! what is this flag
        call    ega_solid_pat
        jmp     cdsc_exit

cdsc_not_solid_nor_grey:
        call    do_wes_patblt
        jmp     cdsc_exit

cdsc_its_inverse_solid_color:
        not     bl
cdsc_its_a_solid_color:
        mov     cx,2
        call    do_solid_patcopy        ;color and accl. flags already in BL
        jmp     short cdsc_exit

cdsc_its_inverse_patblt:
        mov     bl,fr.brush_accel       ;color in lower bits, flags in upper
        test    bl,SOLID_BRUSH
        jnz     short cdsc_its_inverse_solid_color
        test    bl,GREY_SCALE
        jz      short cdsc_blt_not_special_cased
        mov     bl,-1                   ;It's grey.  !!! What is this flag
        call    ega_solid_pat
        jmp     short cdsc_exit

cdsc_its_dn:
        call    do_wes_invert
        jmp     short cdsc_exit

cdsc_its_dpx:
        mov     ah,fr.brush_accel       ;color in lower bits, flags in upper
        test    ah,SOLID_BRUSH
        jnz     cdsc_its_solid_dpx      ;dpx with solid brush
        test    ah,GREY_SCALE           ;Otherwise must be a grey brush
        jz      short cdsc_blt_not_special_cased
        call    do_grey_dpx
        jmp     short cdsc_exit

cdsc_its_solid_dpx:
        call    do_wes_dpx_solidpat     ;solid color => can special case
        jmp     short cdsc_exit


;-----------------------------------------------------------------------;
; This is a source copy.  The phase must be zero to special case the
; source copy, and both devices must be the screen.
;
;       errnz      F0_SRC_IS_DEV - 00001000b
;       errnz    F0_SRC_IS_COLOR - 00000100b
;
; its_src_copy:
;       and     dh,F0_SRC_IS_DEV + F0_SRC_IS_COLOR
;       shiftr  dh,2
;       cmp     fr.phase_h,1            ; Gives CF if horizontal phase = zero
;       rcl     dh,1
; Now we have the needed flags in the lower 3 bits of DH
;                                       ; Src=EGA  Src=Color  Phase0
;
;       dw      do_wes_mono_trick       ;    0        0         0
;       dw      blt_not_a_special_case  ;    0        0         1
;       dw      blt_not_a_special_case  ;    0        1         0
;       dw      blt_not_a_special_case  ;    0        1         1
;       dw      do_wes_mono_trick       ;    1        0         0
;       dw      blt_not_a_special_case  ;    1        0         1
;       dw      blt_not_a_special_case  ;    1        1         0
;       dw      ega_src_copy            ;    1        1         1
;-----------------------------------------------------------------------;

cdsc_its_src_copy:
        test    dh,F0_SRC_IS_DEV
        jnz     short cdsc_exit

cdsc_source_is_memory:
        test    dh,F0_SRC_IS_COLOR          ;mono-mem to color-EGA conversion?
        jnz     cdsc_blt_not_special_cased  ;color to color, cannot special case it
        call    do_wes_mono_trick
        jmp     short cdsc_exit

cdsc_blt_not_special_cased:
        clc
        cRet    check_device_special_cases

cdsc_exit:
        stc
        cRet    check_device_special_cases


;----------------------------Private-Routine----------------------------;
; calc_parms
;
; Calculate the parameters needed for the output functions contained
; in this file.
;
; To avoid conditional jumps we will use some sick optimizations.
; Remember this:
;       adc     ax,-1           ; DEC AX if carry clear
;       sbb     ax,0            ; DEC AX if carry set
;       sbb     ax,-1           ; INC AX if carry clear
;       adc     ax,0            ; INC AX if carry set
;
; Entry:
;       EBP --> BitBLT local frame
; Returns:
;       ESI set to upper left of bitmap or pattern
;       EDI set to upper left
;       EDX = src bitmap width  (if present)
;       ECX = fr.yExt
;       EBX = offset into pattern (if pat present)
;       sets dest_right_edge
;       sets start_mask[0]
;       sets last_mask[0]
;       sets inner_loop_count
; Registers Destroyed:
;       EAX,flags
; Registers Preserved:
;       EBP
; Alters:
;
; Calls:
;       None
;-----------------------------------------------------------------------;

        .errnz  SIZE_PATTERN - 8                ; any power of 2 will work

        align   4
calc_parms:

; Left edge.

        movzx   edi,fr.DestxOrg         ;X origin in pixels, must be positive
        mov     ebx,edi
        mov     cl,7
        and     ecx,edi                 ;Save lower 3 bits
        mov     fr.phase_h,cl
        shr     edi,3                   ;EDI set for left edge
        mov     al,0FFh
        shr     al,cl
        mov     byte ptr fr.start_mask[0],al

; Right edge.

        movzx   eax,fr.xExt
        add     ebx,eax                 ;Right edge in pixels
        mov     cl,7
        and     cl,bl                   ;save lower 3 bits
        shr     ebx,3                   ;convert to bytes
        mov     fr.start_fl,ebx         ;dest_right_edge (reuse stk variable)
        mov     al,0FFh
        shr     al,cl
        not     al

; Check if the BLT does not cross any byte boundaries.

        sub     ebx,edi                 ;make EBX # bytes including left edge
        jnz     crosses_byte_boundary
        and     byte ptr fr.start_mask[0],al
        xor     al,al

; There are 2 cases where we get zero for fr.inner_loop_count:
; When the start and end bytes are adjacent and when they are
; the same byte.  In the latter case we get -1 for
; fr.inner_loop_count so INC BX now so it will be zero.

        inc     ebx
crosses_byte_boundary:

        cmp     al,0FFh
        sbb     al,-1                   ;AL=FF -> AL=0 (put in innerloop)
        mov     byte ptr fr.last_mask[0],al

; Inner loop  --  combine edge bytes into inner loop if they are
; full bytes.

        mov     fr.end_fl,ebx           ;src_right_edge (reuse stk variable)
        mov     al,byte ptr fr.start_mask[0]
        cmp     al,0FFh

; If gl_start_mask = FF the carry is clear, otherwise carry is set.
; We want to DEC BX if carry set because we have already included
; the left edge byte in BX, but we shouldn't have included it if
; it's only a partial byte.

        sbb     ebx,0
        cmp     al,0FFh

; If gl_start_mask = FF the carry is clear, otherwise carry is set.
; We want to INC AL (zero it) if it is FF (carry clear) because we
; will do this edge as part of the innerloop.

        sbb     al,-1
        mov     byte ptr fr.start_mask[0],al
        mov     fr.inner_loop_count,ebx
        movzx   eax,fr.DestyOrg
        imul    eax,ulNextScan_global
        add     edi,eax
        mov     esi,fr.pdsurfDst
        add     edi,[esi].dsurf_pvBitmapStart

; The source.

        test    fr.the_flags,F0_SRC_PRESENT
        jz      no_source
        mov     esi,fr.pdsurfSrc
        mov     ecx,[esi].dsurf_lNextScan   ;!!! will this be correct ??
        mov     esi,[esi].dsurf_pvBitmapStart

; Left edge.

        movzx   ebx,fr.SrcxOrg
        mov     dl,7
        and     dl,bl                   ;get lower 3 bits ( Src Mod 8 )
        sub     fr.phase_h,dl           ;phase def'd as Mod8[gl_dest]-
        shr     ebx,3                   ;               Mod8[gl_src]
        add     esi,ebx                 ;ESI set for left edge
        movzx   eax,fr.SrcyOrg
        mul     ecx
        add     esi,eax
        add     fr.end_fl,esi           ;src_right_edge (reuse stack variable)
        mov     edx,ecx
        jmp     short   no_pattern
no_source:
        test    fr.the_flags,F0_PAT_PRESENT ; assuming P or S but not both
        jz      no_pattern
        mov     esi,fr.lpPBrush
        movzx   ebx,fr.DestyOrg
        and     ebx,SIZE_PATTERN - 1
no_pattern:
        movzx   ecx,fr.yExt
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; ega_solid_pat
;
; EGA special case for solid color pattern copy.
;
; The following routine is invoked instead of generating code for a
; pattern copy.  The actual time involved in executing the pattern
; copy as static code as compared to compiled code is a win.
;
; This code can only be used if the pattern is a solid color or a grey,
; and the operation is to the screen.  In this case, the three bits of
; color stored in the accelerator byte of the brush will be used, or the
; bits of the grey brush.
;
; The logic operations which will invoke this routine are:
;
;       P
;       Pn
;       DDx
;       DDxn
;
; Entry:
;       BL = color to write or xor value for a grey pattern
;       CX = Mode register value (sort of)
;       EBP = BitBLT local variable frame
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL except EBP
; Registers Preserved:
;       EBP
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
ega_solid_pat:

; Instead of pushing and popping the destination pointer and adding in
; the fr.dest.Incr, the bias needed for adjusting the pointer at the
; end of a scan line will be computed and used.
;
; Since this is a pattern copy, the fr.dest.Incr will be positive.

;       mov     esi,edi                 ;Get destination increment
;       sub     esi,1                   ;Adjust for first byte
        lea     esi,-1[edi]
        sub     esi,fr.inner_loop_count ;Compute number of bytes to copy

; Put color in Set/Reset if it is a solid color.

        mov     dx,EGA_BASE + GRAF_ADDR
        or      cx,cx
        jz      not_solid_color
        mov     ax,MM_ALL * 256 + GRAF_ENAB_SR
        out     dx,ax
        mov     ah,bl
        mov     al,GRAF_SET_RESET
        out     dx,ax
not_solid_color:
        mov     al,GRAF_BIT_MASK        ;Leave graphics controller pointing
        out     dx,al                   ;  to the bitmask register, which
        inc     dx                      ;  is where cursor leaves it too

; Set up for the loop.

        mov     edi,fr.dest.lp_bits     ;--> destination
        mov     fr.phase_h,bl           ;Save color to write or grey XOR mask

ega_solid_pat_20:
        mov     al,fr.phase_h               ;Get the color to write
        test    fr.brush_accel,SOLID_BRUSH  ;Grey scale brush?
        jnz     ega_solid_pat_30            ;  No, a solid color
        mov     bl,fr.pat_row               ;Get scan of brush
        inc     bl                          ;  and update brush pointer
        mov     fr.pat_row,bl
        dec     bl
        and     ebx,00000111b
        add     ebx,fr.lpPBrush
        xor     al,bptr [ebx]               ;Invert if needed

ega_solid_pat_30:
        mov     bl,al
        mov     al,bptr fr.start_mask[1]    ;Set bitmask for first byte
        out     dx,al
        mov     al,bl
        xchg    al,[edi]                    ;xchg to load EGA's latches first
        inc     edi                         ;PAT_COPY step +X always!

        mov     ecx,fr.inner_loop_count     ;Set count for innerloop
        jecxz   ega_solid_pat_40            ;No innerloop or last byte
        mov     al,0FFh                     ;Inner loop alters all bits
        out     dx,al
        mov     al,bl
        rep     stosb

ega_solid_pat_40:
        mov     al,bptr fr.last_mask[1]     ;Last byte?
        or      al,al
        jz      ega_solid_pat_50            ;No last byte
        out     dx,al
        xchg    bl,[edi]

ega_solid_pat_50:
        add     edi,esi                     ;--> next destination
        dec     fr.yExt                     ;Any more scans to process?
        jnz     ega_solid_pat_20            ;  Yes
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; do_wes_invert
; do_wes_dpx_solidpat
;
; Entry:
;       EBP --> BitBLT local variable frame
;       AH = color of solid-pat.
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL but EBP
; Registers Preserved:
;       EBP
; Calls:
;       calc_parms
;       edge_invert
;       invert
;-----------------------------------------------------------------------;

        align   4
do_wes_invert:

        mov     ah,0Fh                  ; black

do_wes_dpx_solidpat     label near

; Setup SET_RESET.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_SET_RESET
        out     dx,ax
        mov     ax,0F00h + GRAF_ENAB_SR ; enable all planes
        out     dx,ax

; Go to XOR mode.

        mov     ax,GRAF_DATA_ROT + 256 * DR_XOR
        out     dx,ax

        call    calc_parms
        mov     ah,byte ptr fr.start_mask[0]
        or      ah,ah
        jz      no_left_invert_edge
        push    edi
        call    edge_invert
        pop     edi
        inc     edi

no_left_invert_edge:
        mov     ebx,fr.inner_loop_count
        or      ebx,ebx
        jz      no_inner_invert_loop
        movzx   ecx,fr.yExt
        push    edi
        call    invert
        pop     edi
        add     edi,fr.inner_loop_count

no_inner_invert_loop:
        mov     ah,byte ptr fr.last_mask[0]
        or      ah,ah
        jz      no_last_invert_edge
        movzx   ecx,fr.yExt
        call    edge_invert

no_last_invert_edge:
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
;do_solid_patcopy() is called to copy (PatCopy) a solid brush directly to
;the screen.
;
;Entry:
;   BL      color to write with
;   EDI     fr.dest.next_scan which is equal to width_b
;   EBP     local varible frame
;
;-----------------------------------------------------------------------;

        align   4
do_solid_patcopy:

        cmp     fr.inner_loop_count,0   ;if zero, let ega_solid_pat do the work
        jne     dsp_do_it
        call    ega_solid_pat
        PLAIN_RET

dsp_do_it:
        sub     eax,eax                 ;accumulate flags in AL
        mov     esi,edi                 ;SI: fr.dest.next_scan (must be > 0)
        mov     edi,fr.dest.lp_bits     ;EDI-->first byte to write to
        mov     bh,bptr fr.start_mask[1]
        cmp     ah,bh                   ;is there a left edge?
        rcl     al,1                    ;CY if there is an edge to draw
        sub     bh,0ffh                 ;is the left edge an entire byte wide?
        neg     bh                      ;CY if less than a byte
        rcl     al,1                    ;accumulate flag into AL
        mov     bh,bptr fr.last_mask[1]
        cmp     ah,bh                   ;is there a right edge?
        rcl     al,1                    ;CY if there is an edge to draw
        sub     bh,0ffh                 ;is right edge an entire byte wide?
        neg     bh                      ;CY if less than a byte
        rcl     al,1                    ;accumulate flag into AL
        test    al,04h                  ;set all pixels in left byte?
        jnz     dsp_keep_left_edge      ;no. Do normal stuff
        inc     fr.inner_loop_count     ;left edge is an entire byte. INC inner
        and     al,0f7h                 ;loop cnt and draw it the fast way

dsp_keep_left_edge:
        test    al,01h                  ;set all pixels in right byte?
        jnz     dsp_keep_right_edge     ;no. Do normal stuff
        inc     fr.inner_loop_count     ;need to set all pixels in right byte.
        and     al,0fdh                 ;inc inner loop cnt do it the fast way

dsp_keep_right_edge:
        test    al,0ah                  ;any edges to draw?
        jz      dsp_draw_core_piece     ;no, just do the main chunk

dsp_draw_edges:
        mov     bh,al                   ;save flags in BH
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,MM_ALL * 256 + GRAF_ENAB_SR
        out     dx,ax                   ;enable writing to all planes at once
        mov     ah,bl
        mov     al,GRAF_SET_RESET
        out     dx,ax                   ;program to color value to write
        mov     al,GRAF_BIT_MASK        ;Leave graphics controller pointing
        out     dx,al                   ;  to the bitmask register, which
        inc     dx                      ;  is where cursor leaves it too

        test    bh,08h                  ;need to draw the left edge?
        jz      dsp_draw_right_edge

        push    edi                     ;save destination offset
        mov     al,bptr fr.start_mask[1]
        out     dx,al                   ;get it to the board
        movzx   ecx,fr.yExt

dsp_left_edge_draw_loop:
        mov     al,bl                   ;copy color index into AL
        xchg    al,[edi]                ;load latches, copy color index
        add     edi,esi
        loop    dsp_left_edge_draw_loop
        pop     edi                     ;restore dest offset
        inc     edi                     ;update to new draw position


dsp_draw_right_edge:
        test    bh,02h                  ;is there a right edge to draw?
        jz      dsp_reset_registers     ;no.  Restore default settings
        push    edi                     ;save updated dest offset
        add     edi,fr.inner_loop_count ;go to the right hand edge
        mov     al,bptr fr.last_mask[1]
        out     dx,al                   ;get it to the board
        movzx   ecx,fr.yExt

dsp_right_edge_draw_loop:
        mov     al,bl                   ;copy color index into AL
        xchg    al,[edi]                ;load latches, copy color index
        add     edi,esi
        loop    dsp_right_edge_draw_loop
        pop     edi                     ;restore dest offset


dsp_reset_registers:
        mov     al,0ffh                 ;allow writing to all bits in the byte
        out     dx,al                   ;this is the default value

dsp_draw_core_piece:
        sub     esi,fr.inner_loop_count ;account for EDI being incr. by stosb
        mov     dx,EGA_BASE+SEQ_DATA    ;time to copy pattern to board  DX=3C5h
        mov     al,01h
        mov     ecx,4

dsp_load_latches_loop:
        out     dx,al                   ;select the next plane to write to
        shr     bl,1                    ;move plane bit into carry
        sbb     ah,ah                   ;expand into AH
        mov     [edi],ah                ;copy it to the bit plane
        shl     al,1                    ;update plane selector
        loop    dsp_load_latches_loop   ;do all 4 planes

        mov     al,MM_ALL               ;to enable all four planes
        out     dx,al                   ;enable all planes
        mov     dx,EGA_BASE+GRAF_ADDR   ;DX=3CEh
        mov     ax,GRAF_BIT_MASK        ;AH=0 ie., copy data from latches, AL=8
        out     dx,ax                   ;ignore CPU data on write to board

        mov     al,[edi]                ;load the latches
        movzx   eax,fr.yExt             ;initialize loop counter
        mov     ebx,fr.inner_loop_count ;initialize rep counter value

        align   4
dsp_pat_blt_loop:
        mov     ecx,ebx                 ;ECX: repeat count
        test    edi,1
        jz      short dsp_pat_blt_aligned2
        stosb
        dec     ecx
dsp_pat_blt_aligned2:
        shr     ecx,1
        rep     stosw
        adc     ecx,ecx
        rep     stosb
        add     edi,esi                 ;point to next scanline
        dec     eax
        jnz     dsp_pat_blt_loop

        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; do_wes_patblt
;
; Entry:
;       EBP --> BitBLT local variable frame
; Returns:
;       Nothing
; Registers Destroyed:
;       All but EBP
; Registers Preserved:
;       EBP
; Calls:
;       calc_parms
;       edge_pat_blt
;       pat_blt
;-----------------------------------------------------------------------;

        align   4
do_wes_patblt:

        call    calc_parms
        mov     ah,byte ptr fr.start_mask[0]
        or      ah,ah
        jz      no_left_pat_edge
        push    esi
        push    ebx
        call    edge_pat_blt            ; preserves DI
        pop     ebx
        pop     esi
        inc     edi

no_left_pat_edge:
        mov     edx,fr.inner_loop_count
        or      edx,edx
        jz      no_inner_pat_loop
        movzx   ecx,fr.yExt
        push    edi
        push    esi
        push    ebx
        call    pat_blt
        pop     ebx
        pop     esi
        pop     edi
        add     edi,fr.inner_loop_count

no_inner_pat_loop:
        mov     ah,byte ptr fr.last_mask[0]
        or      ah,ah
        jz      no_last_pat_edge
        movzx   ecx,fr.yExt
        call    edge_pat_blt

no_last_pat_edge:
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; do_wes_mono_trick
;
; Entry:
;       EBP --> BitBLT local variable frame
; Returns:
;       Nothing
; Registers Preserved:
;       EBP
; Registers Destroyed:
;       All but EBP
; Calls:
;       calc_parms
;       left_edge_mono_to_color_blt
;       right_edge_mono_to_color_blt
;       mono_to_color_blt
;-----------------------------------------------------------------------;

        align   4
do_wes_mono_trick:

        call    calc_parms
        push    edx
        mov     ah,byte ptr fr.start_mask[0]
        or      ah,ah
        jz      no_left_edge
        push    edi
        push    esi
        push    edx
        mov     al,fr.phase_h
        mov     bx,fr.both_colors
;-      mov     ecx,fr.yExt
        call    left_edge_mono_to_color_blt
        pop     edx
        pop     esi
        pop     edi
        inc     esi
        inc     edi
no_left_edge:
        mov     ebx,edx
        mov     edx,fr.inner_loop_count
        mov     ecx,edi                   ; compute/save the right-hand edge
        add     ecx,edx
        push    ecx
        or      edx,edx
        jz      no_inner_loop
        sub     ebx,edx
        movzx   ecx,fr.yExt
        mov     al,fr.phase_h
        cbw
        push    ebp
        mov     bp,fr.both_colors
        xchg    bp,ax
        call    mono_to_color_blt
        pop     ebp

no_inner_loop:
        pop     edi
        pop     edx
        mov     ah,byte ptr fr.last_mask[0]
        or      ah,ah
        jz      no_last_edge

        mov     ecx,fr.inner_loop_count
        mov     esi,fr.end_fl           ; src_right_edge (reuse stk variable)
        movzx   ecx,fr.yExt
        mov     bx,fr.both_colors
        mov     al,fr.phase_h
        call    right_edge_mono_to_color_blt

no_last_edge:
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; mono_to_color_blt
;
; This does phase-0, byte-aligned, mem-mono to ega-color blt.
;
; The Problem: copy to the ega a bitmap where "0"s in the bitmap mean
; color1 and "1"s in the bitmap mean color2, where color1 and color2
; are arbitrary colors.
;
; The solution:
;
;                       plane0  plane1  plane2  plane3
;
; color1                1       1       0       0
; color2                1       0       1       0
; SetResetEnable        1       0       0       1
; SetReset              0       x       x       0
; latches               1       1       0       0       (=color1)
;
; Now with datarot = XOR we get
;
;  when databit=0       1       1       0       0       (=color1)
;  when databit=1       1       0       1       0       (=color2)
;
;
; Entry:
;       BP  = phase ( -7 to 7)   (high byte ignored)
;       AL  = background color ( "1" bits in mono-bitmap )
;       AH  = foreground color
;       EBX = SI wrap
;       ESI = Mono Bitmap first byte
;       EDI = First EGA Byte
;       ECX = Number of scan lines
;       EDX = bytes per scan line
;       GRAF_DATA_ROT = DR_SET
;       All Planes Enabled
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL
; Registers Preserved:
;       None
; Alters:
;       GRAF_SET_RESET
;       GRAF_ENAB_SR
;       GRAF_BIT_MASK
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
mono_to_color_blt:

        push    ebp                     ; phase
        push    edx                     ; bytes per scan line
        push    ebx                     ; wrap for SI
        mov     ebx,eax                 ; colors

; First we put the foreground color into the latches.  We do this
; by putting this color into SET_RESET, writing it, then reading it.
; The memory location we will use is the first byte where we will blt.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_SET_RESET
        out     dx,ax
        mov     ax,0F00h + GRAF_ENAB_SR
        out     dx,ax

; Set bit mask = FF.
        mov     ax,0FF00h + GRAF_BIT_MASK
        out     dx,ax

; Fill the latches.
        mov     [edi],al                ; color in SetReset is written, not AL
        mov     al,[edi]                ; read to fill latches

; Go to XOR mode.
        mov     ax,GRAF_DATA_ROT + 256 * DR_XOR
        out     dx,ax

; Now setup SET_RESET.

        mov     eax,ebx                 ; restore colors
        xor     ah,al                   ; gives 0 where colors match
        mov     al,GRAF_SET_RESET
        out     dx,ax
        not     ah
        mov     al,GRAF_ENAB_SR
        out     dx,ax                   ; enable Set/Reset where colors match

        pop     ebp                     ; wrap for ESI
        pop     edx                     ; bytes per scan
        mov     ebx,ulNextScan_global
        sub     ebx,edx                 ; BX = wrap

        mov     eax,ecx                 ; loop count
        pop     ecx                     ; phase
        or      cl,cl
        js      phase_neg
        jz      phase_zero
        dec     esi
;*      dec     ebp
pmono_to_color_loop:
        push    eax
        push    edx
pnext_byte:
        lodsw
        dec     esi
        xchg    al,ah
        shr     ax,cl
;+      shl     ax,cl
        stosb
        dec     edx
        jnz     pnext_byte
        pop     edx
        pop     eax
        add     edi,ebx
        add     esi,ebp
        dec     eax
        jnz     pmono_to_color_loop
        jmp     leave_in_set_mode

phase_zero:
zmono_to_color_loop:
        mov     ecx,edx
        shr     ecx,1
        rep     movsw
        rcl     ecx,1
        rep     movsb
        add     edi,ebx
        add     esi,ebp
        dec     eax
        jnz     zmono_to_color_loop
        jmp     leave_in_set_mode

phase_neg:
        neg     cl                      ; make CX = abs phase
nmono_to_color_loop:
        push    eax
        push    edx
nnext_byte:
        lodsw
        dec     esi
        rol     ax,cl
;+      shr     ax,cl
        stosb
        dec     edx
        jnz     nnext_byte
        pop     edx
        pop     eax
        add     edi,ebx
        add     esi,ebp
        dec     eax
        jnz     nmono_to_color_loop

leave_in_set_mode:
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,GRAF_DATA_ROT + 256 * DR_SET
        out     dx,ax
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; left_edge_mono_to_color_blt
; right_edge_mono_to_color_blt
;
; This problem here is the same as in mono_to_color_blt, except it
; is complicated by the need to preserve what is already in EGA memory
; for part of the byte which we are writing.
;
; We will set the BIT MASK to preserve these bytes.  We will then read
; the data from memory, and write it to the EGA using an XCHG so the
; latches are filled before the write -- so the appropriate EGA bits
; are preserved.
;
; The method for writing the data involves two passes.  The first pass
; writes the data to some of the planes, the second pass writes NOT the
; data to the other planes.  Depending on the two colors involved we
; may be able to skip one of the two passes.
;
; Define BkColor = the color corresponding to "1" bits in the data.
; Define TextColor = the color corresponding to "0" bits in the data.
;
; We will use the Set/Reset register to take care of the planes where
; the colors match.  These planes will be ignored in the rest of this
; comment block.
;
; The first pass writes "1"s where the data is "1".  Therefore, the
; condition for doing the first pass is that the BkColor has a "1"
; somewhere (ignoring those planes taken care of by Set/Reset).
; The second pass does whatever planes remain.  We can skip this pass
; if no planes remain.  To maximize to likelihood of this we make sure
; that all "Set/Reset" planes are enabled on the first pass (if the
; first pass occurs).
;
; Entry:
;       AH  = bitmask
;       AL  = phase (-7 to +7)
;       BH  = foreground color
;       BL  = background color
;       EDX = src bitmap width in bytes
;       ESI = Mono Bitmap first byte
;       EDI = First EGA Byte
;       ECX = Number of scan lines
;       DATA_ROT = DR_SET
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL but EBP
; Registers Preserved:
;       EBP
; Alters:
;       GRAF_SET_RESET
;       GRAF_BIT_MASK
;       GRAF_ENAB_SR
; Calls:
;       None
;-----------------------------------------------------------------------;

; Does left edge, which never requires more than 1 byte, and can fault with
; the 2-byte approach used by the right edge.

        align   4
left_edge_mono_to_color_blt:

        push    ebp
        push    eax              ; AL = phase
        mov     ebp,edx

; Set bit mask.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,ax

; Put foreground color in Set/Reset and enable planes where colors
; match.

        mov     ah,bh
        mov     al,GRAF_SET_RESET
        out     dx,ax
        xor     ah,bl
        not     ah                      ; gives 1 where colors match
        mov     al,GRAF_ENAB_SR
        out     dx,ax
        mov     dx,EGA_BASE + SEQ_DATA  ; The rest of the OUTs are here.
        mov     al,ah
        not     ah                      ; Gives 1 where colors mismatch.

; The following AND leaves 1 bits in AH for the planes which
; CANNOT be done on the second pass.  So if this is zero we can
; skip the first pass.

        and     ah,bl                   ; BL = BkColor = color where data is 1
        or      ah,bl                   ; planes to enable
        mov     ebx,ecx                 ; we're done with the colors in BX
        pop     ecx                     ; phase
        jz      short left_skip_first_pass
        or      al,ah                   ; Include "Set/Reset" planes.
        out     dx,al                   ; Enable planes for first pass.

        push    ecx
        push    esi
        push    edi
        push    eax
        push    ebx
        or      cl,cl
        js      short left_phase_is_negative1
left_pfirst_pass:
        mov     ah,[esi]
        shr     ah,cl
        xchg    ah,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     left_pfirst_pass
        jmp     short left_end_pass_one

left_phase_is_negative1:
        neg     cl                      ; make CL = abs phase
left_nfirst_pass:
        mov     ax,[esi]
        rol     ax,cl
        xchg    al,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     left_nfirst_pass
left_end_pass_one:
        pop     ebx
        pop     eax
        pop     edi
        pop     esi
        pop     ecx

left_skip_first_pass:

; Enable the other planes.

        not     ah
        and     ah,MM_ALL
        jz      short left_no_planes_left
        mov     al,ah
        out     dx,al
        or      cl,cl
        js      short left_phase_is_negative2
left_psecond_pass:
        mov     ah,[esi]
        not     ah
        shr     ah,cl
        xchg    ah,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     left_psecond_pass
        jmp     short left_no_planes_left

left_phase_is_negative2:
        neg     cl                      ; make CL = abs phase
left_nsecond_pass:
        mov     ax,[esi]
        not     ax
        rol     ax,cl
        xchg    al,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     left_nsecond_pass

left_no_planes_left:
        mov     al,MM_ALL
        out     dx,al
        pop     ebp
        PLAIN_RET

; Does right edge, which may require 2 bytes. 2 bytes are always available,
; because if there was only 1 byte across, the left edge would handle it,
; so we don't have to worry about faulting.

        align   4
right_edge_mono_to_color_blt:

        push    ebp
        push    eax              ; AL = phase
        mov     ebp,edx

; Set bit mask.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,ax

; Put foreground color in Set/Reset and enable planes where colors
; match.

        mov     ah,bh
        mov     al,GRAF_SET_RESET
        out     dx,ax
        xor     ah,bl
        not     ah                      ; gives 1 where colors match
        mov     al,GRAF_ENAB_SR
        out     dx,ax
        mov     dx,EGA_BASE + SEQ_DATA  ; The rest of the OUTs are here.
        mov     al,ah
        not     ah                      ; Gives 1 where colors mismatch.

; The following AND leaves 1 bits in AH for the planes which
; CANNOT be done on the second pass.  So if this is zero we can
; skip the first pass.

        and     ah,bl                   ; BL = BkColor = color where data is 1
        or      ah,bl                   ; planes to enable
        mov     ebx,ecx                 ; we're done with the colors in BX
        pop     ecx                     ; phase
        jz      short right_skip_first_pass
        or      al,ah                   ; Include "Set/Reset" planes.
        out     dx,al                   ; Enable planes for first pass.

        push    ecx
        push    esi
        push    edi
        push    eax
        push    ebx
        or      cl,cl
        js      short right_phase_is_negative1
        dec     esi
right_pfirst_pass:
        mov     ax,[esi]
        ror     ax,cl
        xchg    ah,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     right_pfirst_pass
        jmp     short right_end_pass_one

right_phase_is_negative1:
        neg     cl                      ; make CL = abs phase
right_nfirst_pass:
        mov     ax,[esi]
        rol     ax,cl
        xchg    al,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     right_nfirst_pass
right_end_pass_one:
        pop     ebx
        pop     eax
        pop     edi
        pop     esi
        pop     ecx

right_skip_first_pass:

; Enable the other planes.

        not     ah
        and     ah,MM_ALL
        jz      short right_no_planes_left
        mov     al,ah
        out     dx,al
        or      cl,cl
        js      short right_phase_is_negative2
        dec     esi
right_psecond_pass:
        mov     ax,[esi]
        not     ax
        ror     ax,cl
        xchg    ah,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     right_psecond_pass
        jmp     short right_no_planes_left

right_phase_is_negative2:
        neg     cl                      ; make CL = abs phase
right_nsecond_pass:
        mov     ax,[esi]
        not     ax
        rol     ax,cl
        xchg    al,[edi]
        add     esi,ebp
        add     edi,ulNextScan_global
        dec     ebx
        jnz     right_nsecond_pass

right_no_planes_left:
        mov     al,MM_ALL
        out     dx,al
        pop     ebp
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; pat_blt
;                       XOR mode with data = FF for Pn?
;
; This BLTs an arbitrary 8x8 bit pattern (3 or 4 planes deep) to EGA.
;
; The method is simple.  Load the latches with the pattern for a
; particular scan line, then REP STOS this with the BIT MASK = 0
; so that only the latches get written.  Before putting the pattern
; for the next scan line into the latches we will do all other scan
; lines with the same pattern.
;
; Entry:
;       ESI = pattern bytes
;       EDI = First EGA Byte
;       ECX = Number of scan lines (yExt)
;       EBX = offset into pattern
;       EDX = bytes per scan line (scan_len)
;       GRAF_DATA_ROT = DR_SET
;       BIT_MASK = FF
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL but EBP
; Registers Preserved:
;       EBP
; Alters:
;       GRAF_BIT_MASK   (leaves it 00)
; Calls:
;       None
;-----------------------------------------------------------------------;

        .errnz  SIZE_PATTERN - 8        ; actually any power of 2 is okay.

        align   4
pat_blt:

        push    ebp
        push    edx                     ; scan_len
        push    ecx                     ; yExt
        mov     ah,11h                  ; left nibble gives carry to end loop

; Set EBP = min(yExt, scans/pattern).

        sub     ecx,SIZE_PATTERN        ; SIZE_PATTERN = 8 = yExt of pattern
        sbb     ebp,ebp
        and     ebp,ecx
        add     ebp,SIZE_PATTERN

        mov     dx,EGA_BASE + SEQ_DATA
set_next_plane:
        push    ebx
        push    edi

; Enable next plane.

        mov     al,MM_ALL
        and     al,ah
        out     dx,al
        mov     ecx,ebp

hit_next_byte:
        mov     al,[esi][ebx]           ; Next pattern byte
        inc     ebx
        and     ebx,SIZE_PATTERN - 1
        mov     [edi],al
        add     edi,ulNextScan_global
        loop    hit_next_byte
        add     esi,SIZE_PATTERN
        pop     edi
        pop     ebx
        shl     ah,1
        jnc     set_next_plane

; Set bit mask = 00.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0000h + GRAF_BIT_MASK
        out     dx,ax

; Enable all planes.

        mov     al,MM_ALL
        mov     dx,EGA_BASE + SEQ_DATA
        out     dx,al

        mov     ecx,ebp                 ; MIN(yExt,SIZE_PATTERN)
        pop     ebp                     ; yExt
        pop     eax                     ; scan_len
        mov     esi,ulNextScan_global   ; ESI = scan_width

        .errnz  (SIZE_PATTERN - 8)
        ;------------------------------------------------;
        ;       mov     ebx,(SIZE_PATTERN - 1) * scan_width
        ;------------------------------------------------;
        lea     ebx,[esi*2+esi]         ; scan_width * 3
        lea     ebx,[ebx+esi*4]         ; scan_width * 7

        sub     esi,eax                 ; ESI = scan_width
        add     ebx,esi                 ; EBX = (scan_width * 7) + next_scan

pat_blt_next_scan:
        push    ecx
        mov     esi,edi                 ; save ESI
        mov     edx,ebp                 ; save yExt
        mov     cl,[edi]                ; load latches

pat_blt_loop:
        mov     ecx,eax                 ; EAX = scan_len
        rep     stosb
        add     edi,ebx                 ; EBX = (scan_width * 7) + next_scan
        sub     ebp,SIZE_PATTERN
        jg      pat_blt_loop

        mov     ebp,edx
        dec     ebp
        mov     edi,esi
        add     edi,ulNextScan_global
        pop     ecx
        loop    pat_blt_next_scan

        pop     ebp
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; edge_pat_blt
;
; Entry:
;       AH  = bitmask
;       ESI = pattern bytes
;       EDI = First EGA Byte
;       ECX = Number of scan lines (yExt)
;       EBX = offset into pattern
;       DATA_ROT = DR_SET
; Returns:
;       Nothing
; Registers Destroyed:
;       EAX,ECX,EDX,ESI,flags
; Registers Preserved:
;       EBX,EDI,EBP
; Alters:
;       GRAF_BIT_MASK   (leaves it FF)
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
edge_pat_blt:

        push    ebp

; Set bit mask.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,ax
        mov     ah,11h                  ; left nibble gives carry to end loop
        mov     dx,EGA_BASE + SEQ_DATA
        sub     si,SIZE_PATTERN
        mov     ebp,ecx

enable_next_plane:
        push    ebx
        push    edi
        mov     ecx,ebp                 ; yExt
        mov     al,MM_ALL
        and     al,ah
        out     dx,al
        add     esi,SIZE_PATTERN

over_scans:
        mov     al,[ebx][esi]           ; pattern fetch
        inc     ebx
        and     ebx,SIZE_PATTERN - 1    ; 7
        .errnz  SIZE_PATTERN - 8        ; any power of 2 works
        xchg    [edi],al
        add     edi,ulNextScan_global
        loop    over_scans

        pop     edi
        pop     ebx
        shl     ah,1
        jnc     enable_next_plane

; Restore bitmask to default.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out     dx,ax

        pop     ebp
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; invert
;
; Inverts pixels in a rectangle on the display, by simply writing the
; memory to itself, letting the EGA hardware perform the XORing.
;
; Entry:
;       EDI = First EGA Byte
;       ECX = Number of scan lines (yExt)
;       EBX = scan line length in bytes
;       DATA_ROT = DR_XOR
;       GRAF_SET_RESET = color to xor DEST with
;       GRAF_SR_ENAB = MM_ALL
; Returns:
;       Nothing
; Registers Destroyed:
;       EAX,ECX,EDX,ESI,EDI,flags
; Registers Preserved:
;       EBX,EBP
; Alters:
;       GRAF_BIT_MASK   (leaves it FF)
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
invert:

; Set bit mask.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,0FF00h + GRAF_BIT_MASK
        out     dx,ax
        mov     edx,ulNextScan_global
        sub     edx,ebx
        mov     eax,ecx                 ; save height

invert_next_scan:
        mov     esi,edi
        mov     ecx,ebx                 ; scan len in bytes
        rep     movsb
        add     edi,edx
        dec     eax
        jnz     invert_next_scan
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; edge_invert
;
; Inverts one byte on each scan line vertically according to the mask
; in AH.
;
; Entry:
;       AH  = bitmask
;       EDI = First EGA Byte
;       ECX = Number of scan lines (yExt)
;       DATA_ROT = DR_XOR
;       GRAF_SET_RESET = color to xor DEST with
;       GRAF_ENAB_SR = MM_ALL
; Returns:
;       Nothing
; Registers Destroyed:
;       AL,ECX,EDX,EDI,flags
; Registers Preserved:
;       AH,EBX,ESI,EBP
; Alters:
;       GRAF_BIT_MASK
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
edge_invert:

; Set bit mask.

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,ax

edge_invert_next_scan:
        xchg    [edi],al
        add     edi,ulNextScan_global
        loop    edge_invert_next_scan
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; edge_grey_dpx
;
; Inverts one or two bytes on each scan line vertically according
; to the grey pattern given, under the passed clipping mask.
;
; Entry:
;       EBX    =  brush index (0-7)
;       ECX    =  number of scan lines (cyExt)
;       DL     =  lhs clipping mask
;       DH     =  rhs clipping mask
;       ESI   --> base address of brush
;       EDI   --> rhs EGA
;       EBP   --> lhs EGA
; Returns:
;       Nothing
; Registers Destroyed:
;       All
; Registers Preserved:
;       None
; Alters:
;       None
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
edge_grey_dpx:

        sub     ebp,edi                 ;Compute delta to lhs
        and     ebx,00000111b           ;Make sure brush is valid
        .errnz  SIZE_PATTERN - 8
        or      dh,dh                   ;Dispatch based on one or two edges
        jz      edge_grey_dpx_one_loop

edge_grey_dpx_both_loop:
        mov     al,[esi][ebx]           ;Get next byte of brush
        mov     ah,al
        and     al,dl                   ;Mask with lhs clipping mask
        xchg    al,[edi]                ;Invert necessary bits
        inc     ebx                     ;--> next brush byte
        and     bl,00000111b            ;Handle any wrap
        .errnz  SIZE_PATTERN - 8
        and     ah,dh                   ;Mask with rhs clipping mask
        xchg    ah,[edi][ebp]           ;Invert necessary bits
        add     edi,ulNextScan_global   ;--> next destination byte
        loop    edge_grey_dpx_both_loop
        PLAIN_RET

edge_grey_dpx_one_loop:
        mov     al,[esi][ebx]           ;Get next byte of brush
        and     al,dl                   ;Mask with lhs clipping mask
        xchg    al,[edi]                ;Invert necessary bits
        inc     ebx                     ;--> next brush byte
        and     bl,00000111b            ;Handle any wrap
        .errnz  SIZE_PATTERN - 8
        add     edi,ulNextScan_global   ;--> next destination byte
        loop    edge_grey_dpx_one_loop
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; middle_grey_dpx
;
; Inverts a rectangle on the display using the passed grey pattern.
;
; Entry:
;       EBX     =  brush index (0-7)
;       ECX     =  # byte to invert on the scan
;       EDX     =  EGA_BASE + GRAF_ADDR
;       ESI    --> base address of brush
;       EDI    --> starting byte
;       EBP     =  number of scan lines (cyExt)
;       DATA_ROT       = DR_XOR
;       GRAF_SET_RESET = All 1
;       GRAF_ENAB_SR   = MM_ALL
; Returns:
;       Nothing
; Registers Destroyed:
;       EAX,ECX,EDX,ESI,EDI,flags
; Registers Preserved:
;       EBX,EBP
; Alters:
;       GRAF_BIT_MASK   (leaves it FF)
; Calls:
;       None
;-----------------------------------------------------------------------;

        align   4
middle_grey_dpx:

;       mov     dx,EGA_BASE + GRAF_ADDR ; Leave the Graphics controller
        mov     al,GRAF_BIT_MASK        ;   address register pointing to
        out     dx,al                   ;   the bitmask register
        inc     edx                     ; --> Graphics controller data register
        .errnz  GRAF_DATA - GRAF_ADDR - 1

        mov     ah,bl                   ; Keep brush index here
        mov     ebx,ecx                 ; Save a copy of inner loop count here

middle_grey_dpx_loop:
        xchg    eax,ebx
        xchg    bl,bh
        and     ebx,00000111b
        errnz   SIZE_PATTERN-8
        mov     bh,[esi][ebx]           ; Get next byte of brush
        inc     bl                      ; --> byte of the brush
        xchg    bh,bl
        xchg    eax,ebx
        out     dx,al
        mov     ecx,ebx
        push    esi
        mov     esi,edi
        rep     movsb
        pop     esi
        sub     edi,ebx
        add     edi,ulNextScan_global   ;next scan on screen
        dec     ebp
        jnz     middle_grey_dpx_loop
        PLAIN_RET


;----------------------------Private-Routine----------------------------;
; do_grey_dpx
;
; This is EGA special cased code for the dpx raster op in the case where
; the pattern (p) is grey (the same on all planes).  We also come here
; for the special graying pattern rop.  This is not a normal P,S, and D rop,
; but a hack for pmwin.  It allows graying of things on the screen, meaning
; the background color is stuffed everywhere the pattern has a "1" bit.
;
; Entry:
;       EBP --> BitBLT local variable frame
; Returns:
;       Nothing
; Registers Destroyed:
;       ALL but EBP
; Registers Preserved:
;       EBP
; Calls:
;       calc_parms
;       edge_grey_dpx
;       middle_grey_dpx
;-----------------------------------------------------------------------;

        align   4
do_grey_dpx:

        call    calc_parms
        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ax,DR_XOR shl 8 + GRAF_DATA_ROT ; XOR mode for grey dpx
        out     dx,ax
        mov     dl,byte ptr fr.start_mask[0]
        mov     dh,byte ptr fr.last_mask[0]
        or      dx,dx
        jz      do_grey_dpx_middle      ;Only middle bytes exist

        mov     eax,edi                 ;Assume we have a left edge
        inc     eax                     ;+1 to get to start of middle bytes
        or      dl,dl                   ;Is there really a left edge?
        jnz     do_grey_dpx_have_lhs    ;  Yes, AX = correct middle byte start
        dec     eax                     ;  No, restore middle byte start
        xchg    dl,dh                   ;  Pretend only a lhs edge
        add     edi,fr.inner_loop_count ;    but make it be the rhs

do_grey_dpx_have_lhs:
        push    eax                     ;Save middle bytes start
        push    ebx
        push    ecx
        push    esi
        push    ebp
        add     eax,fr.inner_loop_count ;--> possible rhs
        xchg    eax,ebp
        call    edge_grey_dpx
did_gray_pat_edge:
        pop     ebp
        pop     esi
        pop     ecx
        pop     ebx
        pop     edi                     ;Restore middle bytes start

do_grey_dpx_middle:
        mov     eax,fr.inner_loop_count
        or      eax,eax
        jz      do_grey_dpx_exit
        push    ebp
        mov     ebp,ecx
        xchg    eax,ecx
        mov     dx,EGA_BASE + GRAF_ADDR         ;
        mov     ax,MM_ALL shl 8 + GRAF_ENAB_SR  ; Set Set/Reset to all "1" bits
        out     dx,ax                           ; and Enable all planes (for
        .errnz  MM_ALL - 0Fh                    ; grey_dpx)
        mov     al,GRAF_SET_RESET               ;
        out     dx,ax                           ;

gray_pat:
        call    middle_grey_dpx
        pop     ebp

do_grey_dpx_exit:
        PLAIN_RET


endProc check_device_special_cases

_TEXT$01   ends

        end


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.