;*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 = SCANLINE.ASM
;*
;* DESCRIPTIVE NAME = Scanline sub-function of Output
;*
;*
;* VERSION      V2.0
;*
;* DATE         02/22/87
;*
;* DESCRIPTION  This module contains the scanline sub-function of Output. 
;*              
;* FUNCTIONS    PolyScanline
;*              scanline_device_init
;*              predigest_data
;*              look_for_special_cases
;*              scanline_reset_ega
;*              do_shortline_pair
;*              draw_one_plane_of_scan
;*
;* 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/87                     Written by Walt Moore [waltm] and Wes 
;*                                Rupel [wesleyr]
;*   12/08/87                     Walt Moore [waltm] Added kludged test of the 
;*                                COM_DRAW bit for IC support.
;*   04/13/88                     Bob Grudem [bobgru] Added MF_FGNOP_BKOPQ flag.
;*   08/01/88                     Bob Grudem [bobgru] Cursor exclusion done 
;*                                outside of loops, to the bounding box of the
;*                                shortline structure. If all the clipping 
;*                                rectangles are contained in the DDC, then 
;*                                don't call off to get_clip_rects to get them.
;*   02/13/89                     Wes Rupel [wesleyr] Reorganization of code
;*                                for speed.  Stuff removed from per scan subs.
;*   05/08/89                     Lee A. Newberg [leen] Will correlate INFO DC's.
;*   12/20/89                     Viroon Touranachun [viroont] Added 16 
;*                                background mixmodes.
;*****************************************************************************/

        .xlist
        include cmacros.inc
INCL_GRE_CLIP           equ                      1
INCL_GRE_SCANS          equ                      1
INCL_GPIREGIONS         equ                      1
INCL_DDIMISC            equ                      1
INCL_DEV                equ                      1
INCL_DDICOMFLAGS        equ                      1
        include pmgre.inc
        include driver.inc
        include display.inc
        include egafam.inc
        include egamemf.inc
        include scanline.inc
        include assert.mac
        include njmp.mac
        .list

        ??_out  scanline


        errcode <BITMAP_NOT_SELECTED>

        externA  DOSHUGEINCR
        externA  SCREEN_CBSCAN                    ;Screen width in bytes

        externFP FSRSemCheck

sBegin Code
        externW CodeData
        externB Code_rop_data_r                   ;EGA Data Rotate Register value
        externB Code_rop_pen_and                  ;Special EGA Drawing mode pen AND mask
        externB Code_rop_pen_xor                  ;Special EGA Drawing mode pen XOR mask
        externNP do_sl_corr
sEnd   Code

sBegin Data
        externB  ddcInit
        externD  pfnDefRectVisible
        externB  semDriver
sEnd   Data

        externFP far_exclude
        externFP far_unexclude
        externNP get_clip_rects
        externNP enumerate_clip_rects
        externNP MakeBrushValid
        externNP PropagateSysClrChange

sBegin  Code
        assumes cs,Code

        externW CodeData

page

;/***************************************************************************
;*
;* FUNCTION NAME = PolyScanline  
;*
;* DESCRIPTION   = 
;*
;*        Short Line Header
;*              SHORTLINEHEADER struc
;*                  slh_usStyle     dw  ?
;*                  slh_usFormat            dw  ?
;*                  slh_ptsStart    dw  (SIZE POINTS)/2 dup (?)
;*                  slh_ptsStop     dw  (SIZE POINTS)/2 dup (?)
;*                  slh_sxLeft      dw  ?
;*                  slh_sxRight     dw  ?
;*                  slh_pslhNext    dd  ?
;*                  slh_pslhPrev    dd  ?
;*              SHORTLINEHEADER ends
;*        Short Line
;*              SHORTLINE struc
;*                  sl_slh          dw  (size SHORTLINEHEADER)/2 dup (?)
;*                  sl_ax           dw  1 dup (?)
;*              SHORTLINE ends
;*        ScanData
;*              SCANDATA struc
;*                  sd_pslFirstLeft  dd  ?
;*                  sd_pslLastLeft   dd  ?
;*                  sd_pslFirstRight dd  ?
;*                  sd_pslLastRight  dd  ?
;*                  sd_c       dd  ?
;*                  sd_rclBound      dw  (SIZE RECTL)/2 dup (?)
;*              SCANDATA ends
;*
;*              Calls:
;*                    comp_scan_for_bitmap
;*                    get_fill_data
;*                    comp_interval
;*                    unexclude
;*                    various drawing functions set up by other code
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1,2 = Good,Good/Correlation 
;* RETURN-ERROR  = AX = 0
;*
;**************************************************************************/

        check   PolyScanline,<hdc,psd,hddc,ulFunN>

        assumes ds,nothing
        assumes es,nothing

cProc   PolyScanline,<PUBLIC,FAR,NODATA>,<di,si>

        parmD   hdc
        parmD   pScansData
        parmD   hddc
        parmD   FunN

        localD  arg_lp_ddc                        ;--> device display context

        localD  loc_right_sl
        localD  loc_left_sl
        localD  loc_right_sl_ini
        localD  loc_left_sl_ini
        localD  loc_right_sl_x
        localD  loc_left_sl_x
        localD  loc_points
        localW  loc_sl_count
        localW  loc_sl_count_ini
        localW  loc_scan_count                    ; # of deltas in PSL
        localW  loc_saved_y                       ; Scan's Y coordinate (our perspective)
        localW  loc_saved_y_unfliped              ; Scan's Y coord   (Engine perspective)
        localW  loc_next_plane                    ; Index to next plane of the scan
        localW  loc_raster_op                     ; Rop2, mapped 0==>15
        localW  loc_bk_raster_op                  ; Rop of BackGround
        localW  loc_loop_control                  ; Looping control flag
        localW  loc_fetch_data                    ; Control parameters for interval calc
        localW  loc_bm_height
        localW  loc_current_rect
        localD  loc_scan_start
        localV  loc_control,%(size EXTENDED_CONTROL)
        localV  loc_processors,%(BITS_PEL * size cntrl_blk)
        localV  loc_bk_processors,%(BITS_PEL * size cntrl_blk)
        localW  loc_proc_offset                   ; Offset to the first control block elem
        localB  loc_sd_fb                         ; local copy of surface def. flags
        localW  loc_transparency_mask             ; Mask of altered bits within a byte
        localW  loc_bk_transparency_mask          ; Mask of altered bits within a byte
        localB  loc_some_flags                    ; Some flags
        localB  loc_moore_flags                   ; Moore flags
MF_LAST_PASS     equ    00000001b
MF_KILL_SL       equ    00000010b
MF_UNEXCLUDE     equ    00100000b
MF_USE_DDC_RECTS equ    01000000b
MF_SPECIAL_SOLID equ    10000000b
        localB  loc_clean_up                      ; Flags for cleaning up at exit
        localW  loc_first_mask
        localW  loc_last_mask
        localW  loc_inner_loop_count
        localW  retcode                           ;return code
cBegin
        fw_zero <ds,es>

        cld
        mov     ds,CodeData
        assumes ds,Data

;/*
;** PolyScanline only makes sense with the device locked, check it out!
;*/

ifdef FIREWALLS
        cCall   FSRSemCheck,<DataOFFSET semDriver>
        or      ax,dx
        jz      @F
        rip     text,<PolyScanLine called without LockDevice>
@@:
endif

        mov     si,hddc.lo                        ; DS:[SI] --> DDC
        ddc?    si
        mov     arg_lp_ddc.hi,ds
        mov     arg_lp_ddc.lo,si

        mov     ax,[si].ddc_iSysClr
        cmp     ax,ddcInit.ddc_iSysClr
        jz      sys_colors_are_good
        cCall   PropagateSysClrChange
        jmp     short must_rerealize_brush
sys_colors_are_good:
        test    [si].ddc_pa.pa_ba.ba_fb,BA_REREALIZE
        jz      scanline_brush_ok
must_rerealize_brush:
        lea     bx,[si].ddc_pa.pa_ba
        xor     cl,cl
        cCall   MakeBrushValid,<ax,ax>               ;unused parameter
        js      scanline_error_exit                  ;Error logged by MakeBrushValid
scanline_brush_ok:

        mov     retcode,1                            

        test    [si].ddc_fb,DDC_PRESENT
        jnz     scanline_bitmap_selected
        mov     ax,PMERR_BITMAP_NOT_SELECTED
        save_error_code
scanline_error_exit:
        xor     ax,ax
        jmp     scanline_get_out
scanline_bitmap_selected:

        cmp     [si].ddc_crcsClip,0
        jnz     scanline_visible
scanline_short_exit:
        jmp     get_rc_and_leave
scanline_visible:

        test    FunN.hi,COM_CORRELATE
        jz      scanline_correlate_done
        mov     cx,RECTDIR_LFRT_BOTTOP-1
        lea     di,pScansData
        mov     ax,pScansData.off
        add     ax,sd_rclBound
        farPtr  myclip,pScansData.sel,ax
        cCall   enumerate_clip_rects,<si,myclip,CodeOFFSET do_sl_corr>
        jcxz    scanline_correlate_done
        mov     retcode,cx
scanline_correlate_done:
        
        test    [si].ddc_fb,DDC_VISIBLE          ; rejects info ddc's
        jz      scanline_short_exit
        test    FunN.hi,COM_DRAW
        jz      scanline_short_exit


        mov     loc_moore_flags,0                ; clear all the moore flags

;/*
;** If the DDC contains all the clipping rectangles we'll need, set
;** a flag.  Doing this here lets us test a bit later on rather than
;** load DS:SI with the DDC twice.
;*/

        mov     ax,[si].ddc_crcsClip
        or      ax,ax
        njz     outahere                          ; no clip rects --> nothing visible
        cmp     ax,NUM_CLIP_RECTS
        ja      @F
        or      loc_moore_flags,MF_USE_DDC_RECTS
@@:

        mov     si,[si].ddc_npsd                 ; get surface definition from ddc
        mov     cx,[si].sd_cy                     ; Save for flipping Y coord
        mov     loc_bm_height,cx

;/*
;** If we're drawing to the device, then exclude the cursor from the
;** area to receive the bits.
;*/

        mov     al,[si].sd_fb
        mov     loc_sd_fb,al                      ; save sd_fb for later
        call    predigest_data
        njc     outahere
        test    loc_sd_fb,SD_DEVICE
        jz      @F
        call    scanline_device_init              ; cursor exclusion, etc.
        call    look_for_special_cases
@@:
        lds     si,pScansData                     ; DS:SI --> ScanData structure
        assumes ds,nothing
        mov     ax,word ptr [si].sd_c[0]
        mov     loc_sl_count_ini,ax

;/*
;** If we're to get clip rects from the DDC, skip the initialization
;** of the loc_control variable.
;*/

        test    loc_moore_flags,MF_USE_DDC_RECTS
        jnz     copy_bounding_rect

;/*
;** There are more clip rects than fit in the DDC, so we have to call away
;** to get them in batches.  To do this we must set up some fields in the
;** ec_control structure that aren't needed if the rects come from the DDC.
;**
;** Fill in the Control Structure needed by get_clip_rects
;**
;** The field rgnrc_ircStart is updated at the bottom of the loop before
;** calling for the next batch of rects.
;**
;** DS:SI --> ScanData structure
;*/

        mov     loc_control.ec_control.rgnrc_ircStart,1
        mov     loc_control.ec_control.rgnrc_crc,NBR_RECTS
        mov     loc_control.ec_control.rgnrc_usDirection,RECTDIR_LFRT_BOTTOP


;/*
;** Copy the scanline data bounding rectangle into our stack frame.  We'll
;** need it later to pass to get_clip_rects, or to do our own intersections
;** with clipping rectangles.
;*/

copy_bounding_rect:
        mov     ax,[si].sd_rclBound.rcl_xLeft.lo
        mov     dx,[si].sd_rclBound.rcl_xLeft.hi
        mov     loc_control.ec_rect_bound.rcl_xLeft.lo,ax
        mov     loc_control.ec_rect_bound.rcl_xLeft.hi,dx

        mov     ax,[si].sd_rclBound.rcl_yBottom.lo
        mov     dx,[si].sd_rclBound.rcl_yBottom.hi
        mov     loc_control.ec_rect_bound.rcl_yBottom.lo,ax
        mov     loc_control.ec_rect_bound.rcl_yBottom.hi,dx

        mov     ax,[si].sd_rclBound.rcl_xRight.lo
        mov     dx,[si].sd_rclBound.rcl_xRight.hi
        mov     loc_control.ec_rect_bound.rcl_xRight.lo,ax
        mov     loc_control.ec_rect_bound.rcl_xRight.hi,dx

        mov     ax,[si].sd_rclBound.rcl_yTop.lo
        mov     dx,[si].sd_rclBound.rcl_yTop.hi
        mov     loc_control.ec_rect_bound.rcl_yTop.lo,ax
        mov     loc_control.ec_rect_bound.rcl_yTop.hi,dx


;/*
;** Save the location of the first shortlines.
;*/

        les     di,[si].sd_pslFirstLeft
        assumes es,nothing
        lds     si,[si].sd_pslFirstRight
        assumes ds,nothing
        mov     loc_right_sl_ini.sel,ds            ; DS:SI --> Right ShortLine struct
        mov     loc_right_sl_ini.off,si
        mov     loc_left_sl_ini.sel,es             ; ES:DI --> Left ShortLine struct
        mov     loc_left_sl_ini.off,di

;/*
;** If we're to get clip rects from the DDC, then go get them.
;*/

        test    loc_moore_flags,MF_USE_DDC_RECTS
        njz     get_rects_from_engine

;/*
;** We have all the rectangles we'll ever need right in the DDC.
;** Check for intersection of each clip rectangle with the scan data
;** bounding box.  Each intersecting rect gets copied into the ec_buffer
;** just as the engine would do it.  Also have to fill in rgnrc_crcReturned
;** for the fake-out to work.
;**
;** This code assumes that the clip rectangles in the DDC are stored
;** in order of moving up and to the right.
;*/

;/*
;** More work to do if these constants aren't the same.  NBR_RECTS is
;** the size of the batch the engine gives back.   NUM_CLIP_RECTS is the
;** number of rects stored in the DDC.
;*/

        .errnz  NBR_RECTS - NUM_CLIP_RECTS

;/*
;** Source is RECTS, destination is RECTL:
;*/
        .errnz  (SIZE ec_buffer / SIZE RECTL) - NBR_RECTS

        lds     si,arg_lp_ddc
        ddc?    si

        mov     ax,[si].ddc_crcsClip
        assert  ax,NE,0
        assert  ax,BE,NUM_CLIP_RECTS

        lea     di,loc_control.ec_buffer
        CPUMode 386
        lgs     si,[si].ddc_prddc                ;GS:SI = RDDC
        assumes gs,nothing
        add     si,rddc_arcsClip

        xor     bx,bx                             ;assume no intersections

try_intersection:
        mov     dx,gs:[si].rcs_xLeft
        cmp     dx,loc_control.ec_rect_bound.rcl_xRight.lo
        jge     next_rect

        mov     dx,gs:[si].rcs_xRight
        cmp     dx,loc_control.ec_rect_bound.rcl_xLeft.lo
        jle     next_rect

        mov     dx,gs:[si].rcs_yBottom
        cmp     dx,loc_control.ec_rect_bound.rcl_yTop.lo
        jge     next_rect

        mov     dx,gs:[si].rcs_yTop
        cmp     dx,loc_control.ec_rect_bound.rcl_yBottom.lo
        jle     next_rect

        inc     bx                                ;we had another intersection

        assert  ND
        push    ax
        push    si
        mov     cx,(size RECTS)/2
rect_copy_loop:
        lods    word ptr gs:[si]
        cwd
        mov     ss:[di].lo,ax
        mov     ss:[di].hi,dx
        add     di,(size POINTL)/2               ;moving one long coord at a time
        loop    rect_copy_loop
        pop     si
        pop     ax

        CPUMode 286
next_rect:
        add     si,size RECTS
        dec     ax
        jnz     short try_intersection

        or      bx,bx
        jz      all_done_already

        mov     loc_control.ec_control.rgnrc_crcReturned,bx
        jmp     short got_the_rects

;/*
;** send_more_clip_rects
;**
;** This is the top of the outermost loop, where we call for batches of
;** clipping rectangles.  The field rgnrc_ircStart will have been set
;** as needed.
;*/

        assumes ds,nothing
        assumes es,nothing

        public  send_more_clip_rects
send_more_clip_rects:
get_rects_from_engine:
        lds     si,arg_lp_ddc
        assumes ds,Data
        ddc?    si

        push    bp
        lea     bp,loc_control                    ; SS:BP -> extended control structure
        cCall   get_clip_rects                    ; fill ec_buffer with clip rects
        pop     bp

;/*
;** Now check how many rects we got.  If the buffer is not full then
;** we will set a flag to say this is the last pass -- we should not
;** call for more rects again.  If it returned no rects then we are
;** already done.
;*/

        mov     cx,loc_control.ec_control.rgnrc_crcReturned
        cmp     cx,NBR_RECTS
ifdef FIREWALLS
        jbe     its_good_joe
        rip     text,<More rectangles were returnd from get_clip_rects than were asked for>
its_good_joe:
endif
        jz      buffer_is_full
        jcxz    all_done_already                 ; no more rects

;/*
;** If all the clipping rectangles were in the DDC, then we'll get here
;** without having called the engine, and we never expect to come back here.
;** We enforce this by setting the Moore flag to indicate this is the last
;** pass over the clip rects.
;**
;** We also get here if we called the engine for clip rects and it didn't
;** fill our buffer.
;*/

got_the_rects:
        or      loc_moore_flags,MF_LAST_PASS
buffer_is_full:
        lea     ax,loc_control.ec_buffer
        mov     loc_current_rect,ax

do_scans_data:
        mov     ax,loc_sl_count_ini
        mov     loc_sl_count,ax

        les     di,loc_left_sl_ini               ; ES:DI --> Left ShortLine
        lds     si,loc_right_sl_ini              ; DS:SI --> Right ShortLine
        jmp     short   got_both_sls


;/*
;** Another one of those ubiquitous short jump way-stations.
;*/

all_done_already:                                ; no more rects
        jmp     outahere


;/*
;** Get the next shortline structures for right and left sides.
;*/

;/*
;**bad_sl_count:
;**
;**; We have hit the end of the linked list of shortlines (null terminator)
;**; but the count specified in ScanData indicates there is more.

        public  next_shortline
next_shortline:
        lds     si,loc_right_sl                   ; current Right ShortLine struct
        les     di,loc_left_sl                    ; current Left ShortLine struct
        lds     si,ds:[si].slh_pslhNext           ; next     Right ShortLine struct
        les     di,es:[di].slh_pslhNext           ; next     Left ShortLine struct

;/*
;** end of linked list of shortlines?
;*/


got_both_sls:

;/*
;** Save the location of our new shortlines.
;*/

        mov     loc_right_sl.sel,ds              ; DS:SI --> Right ShortLine struct
        mov     loc_right_sl.off,si
        mov     loc_left_sl.sel,es               ; ES:DI --> Left ShortLine struct
        mov     loc_left_sl.off,di

        test    loc_moore_flags,MF_KILL_SL
        jz      no_kill_to_do

;/*
;**  We must blow off the previous shortline-pair, never to be referenced
;**  again.  We do this by advancing the "ini" local variables.
;*/

        and     loc_moore_flags,not MF_KILL_SL
        dec     loc_sl_count_ini
        mov     loc_right_sl_ini.sel,ds          ; DS:SI --> Right ShortLine struct
        mov     loc_right_sl_ini.off,si
        mov     loc_left_sl_ini.sel,es           ; ES:DI --> Left ShortLine struct
        mov     loc_left_sl_ini.off,di
no_kill_to_do:

        assert  [si].slh_usFormat,E,SLH_FORMAT_IS_16_DOT_16
        assert  es:[di].slh_usFormat,E,SLH_FORMAT_IS_16_DOT_16

;/*
;** - - - - - - - - Get Start/Stop Y values - - - - - - - - - - - - - -
;** The Start and Stop Y values should match on the right and left.
;** If they don't we use a FireWall to catch Engine bugs.  Then we return
;** an error.
;*/

        mov     ax,[si].slh_ptsStart.pts_y       ; Check that start and stop
        assert  ax,E,es:[di].slh_ptsStart.pts_y  ; Y coords match
        jmp     same_start_y

skip_this_shortline_forevermore:
        or      loc_moore_flags,MF_KILL_SL
skip_this_shortline:
        dec     loc_sl_count
        jnz     next_shortline
done_with_this_clip_rect:
        jmp     done_with_scans_data

same_start_y:
        xchg    bx,ax
        mov     ax,[si].slh_ptsStop.pts_y         ; BX = StartY, AX = StopY
        assert  ax,E,es:[di].slh_ptsStop.pts_y

        sub     ax,bx                             ; scan count = StopY - StartY
        mov     loc_scan_count,ax

        lea     ax,[bx-1]                         ; -1 to balance INC below.
        mov     loc_saved_y_unfliped,ax
        mov     ax,loc_bm_height
        sub     ax,bx                             ; flip Y coord (sd_cbScan - StartY)
        mov     loc_saved_y,ax

;/*
;**---------------------------------------------------------------------------
;** If this shortline-pair is completely out outside the clip rect then
;** we will skip to the next shortline-pair:
;**
;** bottom of shortline > top of clip-rect    =>   finished with this clip-rect
;** bottom of clip-rect > top of shortline    =>   skip this shortline forevermore
;** left   of shortline > right of clip-rect  =>  skip this shortline
;** left   of clip-rect > right of shortline  =>  skip this shortline
;**
;** By "forevermore" I mean "now and for all future rects".  We can do
;** this because the shortlines go right-then-up, as do the rects.
;** (we chose RECTDIR_LFRT_BOTTOP for the clip-rects for this reason).
;** To accomplish this we will advance the "ini" loc_ variables past
;** this shortline-pair.
;**
;** BX                  > top of clip-rect         =>  finished with this clip-rect
;** bottom of clip-rect > [si].slh_ptsStop.pts_y  =>  skip this shortline forevermore
;** es:[di].slh_sxLeft  > right of clip-rect       =>  skip this shortline
;** left   of clip-rect > ds:[si].slh_sxRight      =>  skip this shortline
;**
;** BX still has loc_saved_y_unfliped which (at this point) is the bottom scan
;**---------------------------------------------------------------------------
;*/

        xchg    ax,bx                            ; bottom of shortline -> AX
        mov     bx,loc_current_rect

;/*
;** bottom of shortline > top of clip-rect  =>  finished with this clip-rect
;*/
        cmp     ax,ss:[bx].rcl_yTop.lo            ; top of clip-rect
        jg      done_with_this_clip_rect

;/*
;** bottom of clip-rect > top of shortline  =>  skip this shortline forevermore
;*/
        mov     ax,ss:[bx].rcl_yBottom.lo        ; bottom of clip-rect
        cmp     ax,[si].slh_ptsStop.pts_y
        jg      skip_this_shortline_forevermore

;/*
;** left of shortline > right of clip-rect  =>  skip this shortline
;*/
        mov     ax,es:[di].slh_sxLeft
        cmp     ax,ss:[bx].rcl_xRight.lo         ; right of clip-rect
        jg      skip_this_shortline

;/*
;** left of clip-rect > right of shortline  =>  skip this shortline
;*/
        mov     ax,ss:[bx].rcl_xLeft.lo          ; left of clip-rect
        cmp     ax,[si].slh_sxRight
        jg      skip_this_shortline

        add     si,sl_ax                          ; --> first scan right end
        add     di,sl_ax                          ; --> first scan left end

;/*
;** Bottom of clip-rect minus StartY gives deltaY to first visible scan.
;*/

        mov     ax,ss:[bx].rcl_yBottom.lo         ; bottom of clip-rect
        dec     ax
        mov     dx,ax
        sub     ax,loc_saved_y_unfliped           ; 36h - 35h
        jle     first_shortline_scan_visible
        mov     loc_saved_y_unfliped,dx           ; update StartY to first visible scan
        sub     loc_scan_count,ax                 ; AX = DeltaY
        sub     loc_saved_y,ax
        add     ax,ax
        .errnz  (TYPE sl_ax) - 2
        add     si,ax
        add     di,ax

first_shortline_scan_visible:

        mov     loc_right_sl_x.hi,ds                    ; DS:SI --> Right ShortLine
        mov     loc_right_sl_x.lo,si
        mov     loc_left_sl_x.hi,es                     ; ES:DI --> Left ShortLine
        mov     loc_left_sl_x.lo,di
        cCall   do_shortline_pair
        dec     loc_sl_count
        njnz    next_shortline

done_with_scans_data:                            ; for this clip-rect

;/*
;** Check if another clip-rect exists
;*/

        add     loc_current_rect,SIZE RECTL      ; advance for next rect
        dec     loc_control.ec_control.rgnrc_crcReturned ; dec rect_count
        jz      finished_rect_buffer
        jmp     do_scans_data                    ; another rect exists
finished_rect_buffer:
        test    loc_moore_flags,MF_LAST_PASS
        jnz     outahere
        add     loc_control.ec_control.rgnrc_ircStart,NBR_RECTS
        jmp     send_more_clip_rects
outahere:
        call    scanline_reset_ega
        test    loc_moore_flags,MF_UNEXCLUDE
        jz      get_rc_and_leave
        cCall   far_unexclude
get_rc_and_leave:
        mov     ax,retcode                        ; Make AX nonzero to show success
scanline_get_out:
        cwd
        fw_zero <ds,es>
cEnd


;/***************************************************************************
;*
;* PUBLIC ROUTINE   scanline_device_init   
;*
;* DESCRIPTION   =  Cursor Exclusion, setting of CleanUp flags, etc.
;*
;*                  Registers Preserved:                 
;*                        BP                             
;*                  Registers Destroyed:                 
;*                        AX,BX,CX,DX,SI,DI,DS,ES,FLAGS  
;*
;* INPUT         = NONE
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  scanline_device_init
scanline_device_init      proc                     near

;/*
;** Set the clean_up flags:
;** CU_EXCLUDE      Shows that the exclusion rectangle may have been set
;** CU_PLANE_SEL    EGA plane selection will be required in the main loop
;**                 (if we determine otherwise later, we'll clear it).
;** CU_REGS         Reset EGA to default state upon exit.
;*/

        mov     loc_clean_up,CU_EXCLUDE+CU_PLANE_SEL+CU_REGS
        mov     loc_next_plane,0                    ; Index to next plane == 0 for EGA

;/*
;** Selector of our drawing surface will not change since it is the device
;** and the surface is not huge.                   Therefore we can fill it in now:
;*/
        lds     si,arg_lp_ddc
        assumes ds,nothing
        ddc?    si
        mov     si,[si].ddc_npsd
        mov     dx,[si].sd_pBits.sel
        mov     loc_scan_start.sel,dx

;/*
;** Use the scandata bounding box as the exclusion rectangle.
;** !!! This code assumes that the bounding rect consists of 16-bit integers.
;*/

        lds     bx,pScansData                     ; DS:SI --> ScanData structure
        assumes ds,nothing
        mov     ax,[bx].sd_rclBound.rcl_xLeft.lo
        mov     si,[bx].sd_rclBound.rcl_xRight.lo
        mov     di,[bx].sd_rclBound.rcl_yBottom.lo
        mov     bx,[bx].sd_rclBound.rcl_yTop.lo

;/*
;** Clip all the coordinates to 0.
;*/

        cwd
        not     dx
        and     ax,dx
        xchg    ax,cx                             ; CX = left

        xchg    ax,bx
        cwd
        not     dx
        and     ax,dx
        xchg    ax,bx                             ; BX = top

        xchg    ax,si
        cwd
        not     dx
        and     ax,dx
        xchg    ax,si                             ; SI = right

        xchg    ax,di
        cwd
        not     dx
        and     ax,dx
        xchg    ax,di                             ; DI = bottom

;/*
;** Flip the y coordinates.
;*/

        neg     bx
        add     bx,loc_bm_height
        neg     di
        add     di,loc_bm_height

        mov     dx,bx                             ; top is supposed to be here
        cCall   far_exclude                       ; destroys all registers except BP
        or      loc_moore_flags,MF_UNEXCLUDE
        ret
scanline_device_init      endp

page

;/***************************************************************************
;*
;* PUBLIC ROUTINE  predigest_data
;*
;* DESCRIPTION   = Get All Data Required For Filling The Scan 
;*
;*                 Registers Preserved:             
;*                       ES,BP                      
;*                 Registers Destroyed:             
;*                       AX,BX,CX,DX,SI,DI,DS,FLAGS 
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = 'C' Carry Flag Set if nothing to do.  Clear means continue.
;* RETURN-ERROR  = 'C' set if to abort 
;*                 DS:DI = ddc         
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

                public  predigest_data
predigest_data   proc near

        lds     di,arg_lp_ddc
        assumes ds,nothing
        ddc?    di

;/*
;** Get the foreground and background mix modes.   If they are both
;** LeaveAlone, then there is no work to do for this scanline,
;** so return with the carry flag set.
;*/

        mov     dx,word ptr [di].ddc_pa.pa_ba.ba_bmix ;Get the foreground
        mov     al,dh                           ; and background mix mode
        .errnz  ba_bkmix-ba_bmix-1
        and     dx,000Fh                           ; (play it safe) ;!!!magic#
        mov     loc_raster_op,dx                  ;save a copy of foregnd mixmode
        and     ax,000Fh                           ; (play it safe) ;!!!magic#
        mov     loc_bk_raster_op,ax               ;save a copy of backgnd mixmode

        cmp     dl,ROP_D                           ;is foreground LeaveAlone?
        jne     pdd_work_to_do                     ;if not, can't exit early
        cmp     al,ROP_D                        ;is background LeaveAlone?
        stc
        jz      pdd_exit                           ;if so exit early

;/*
;**-----------------------------------------------------------------------;
;** Set loc_loop_control to MONO_OP or COLOR_OP to indicate number of
;** planes to draw to.  Also set INDEX_MONO in loc_some_flags.
;**
;** loc_loop_control can change on a scanline basis depending on what the
;** background bits are for that scan, etc (special casing allows us to
;** sometimes treat the EGA as a MONO device).
;** That is why we keep INDEX_MONO in loc_some_flags; it won't change.
;**-----------------------------------------------------------------------;
;*/

pdd_work_to_do:
        mov     ax,MONO_OP + (INDEX_MONO shl 8)
        test    loc_sd_fb,SD_COLOR               ; Is dest color?
        jz      @F
        mov     ax,COLOR_OP + (0 shl 8)
@@:
        mov     loc_some_flags,ah                ; Save the dispatch index for later
        xor     ah,ah
        mov     loc_loop_control,ax              ; loop control flags (count)

        clc
pdd_exit:
        ret
predigest_data   endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE look_for_special_cases  
;*
;* DESCRIPTION   = Set up the EGA/VGA regs appropriately if a special case
;*                 exists.  Currently we just do "solid color".
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

                public  look_for_special_cases
look_for_special_cases   proc near
        lds     di,arg_lp_ddc
        assumes ds,nothing
        ddc?    di
        test    [di].ddc_pa.pa_fb,PA_SINGLE_CLR ; Solid hardware Color?
        jz      sc_exit

sc_check_forground:
        cmp     loc_raster_op,ROP_P
        jnz     sc_check_background

        mov     bx,SIZE_PATTERN - 1
@@:     cmp     [di].ddc_pa.pa_abMask[bx],0FFh    ; TransparencyMask
        jnz     sc_check_background               ; Maybe background
        dec     bx
        jns     @B                                ; use repz scasb?
        xor     si,si                             ; index to foregrnd blocks
        jmp     short sc_solid_brush

sc_check_background:
        cmp     loc_bk_raster_op,ROP_P
        jnz     sc_exit

        mov     bx,SIZE_PATTERN - 1
@@:     cmp     [di].ddc_pa.pa_abMask[bx],00h     ; TransparencyMask
        jnz     sc_exit                           ; no spec case if foregrnd bits
        dec     bx
        jns     @B                                ; use repz scasb?
        mov     si,-(BITS_PEL * (SIZE cntrl_blk)) ; index to backgrnd blocks


;/*
;** Set the control block parameters for both foreground and background
;*/

        public  sc_solid_brush
sc_solid_brush:
        mov     loc_processors[MONO_DATA][si].first_last_proc,CodeOFFSET ega_first_last ; is stosb OK?!
        mov     loc_processors[MONO_DATA][si].inner_loop_proc,CodeOFFSET ega_inner_stosb
        and     loc_processors[MONO_DATA][si].nop_flag,not NF_NOP
        add     si,BITS_PEL * (SIZE cntrl_blk)
        neg     si                              ; switch to the other one
        mov     loc_processors[MONO_DATA][si].first_last_proc,CodeOFFSET do_nothing
        mov     loc_processors[MONO_DATA][si].inner_loop_proc,CodeOFFSET do_nothing
        or      loc_processors[MONO_DATA][si].nop_flag,NF_NOP

;/*
;** Set up the EGA/VGA regs here we will be painting a solid color.
;*/

        mov     dx,EGA_BASE + GRAF_ADDR
        mov     ah,[di].ddc_pa.pa_ba.ba_ipc.ipc_bClr
        and     ah,MM_ALL
        mov     al,GRAF_SET_RESET
        out16   dx,ax
        mov     ax,MM_ALL shl 8 + GRAF_ENAB_SR
        out16   dx,ax                             ;Enable all planes for set/reset

;/*
;** Leave the Graphics Controller address register pointing to
;** the Bitmask register, show that the operation must occur,
;** that plane selection isn't needed, and that only one loop is required.
;*/
        mov     dl,GRAF_ADDR
        mov     al,GRAF_BIT_MASK
        out     dx,al
        and     loc_clean_up,not CU_PLANE_SEL
        mov     loc_loop_control,MONO_OP
        mov     loc_fetch_data,PSL_BYTE_FETCH
        or      loc_moore_flags,MF_SPECIAL_SOLID
sc_exit:
        ret
look_for_special_cases   endp


;/***************************************************************************
;*
;* PUBLIC ROUTINE  scanline_reset_ega  
;*
;* DESCRIPTION   = Restores EGA to default state.  To be called after last   
;*                 use of EGA.                                               
;*                                                                           
;*                 Registers Destroyed:                                                            
;*                       AX,DX           
;*                 
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

        public  scanline_reset_ega
scanline_reset_ega      proc                     near
        mov     al,loc_clean_up
        or      al,al
        jz      no_clean_up_needed
        .errnz  CU_NONE
        jns     no_clean_up_needed               ; No registers need cleaning up
        .errnz  CU_REGS - 80h

        mov     dx,EGA_BASE + SEQ_DATA
        mov     ax,MM_ALL
        out     dx,al
        mov     dl,GRAF_ADDR
        mov     al,GRAF_ENAB_SR
        out16   dx,ax
        mov     al,GRAF_DATA_ROT
        out16   dx,ax
        errnz   DR_SET
        mov     ax,0FF00h+GRAF_BIT_MASK
        out16   dx,ax
no_clean_up_needed:
        ret
scanline_reset_ega      endp

;/***************************************************************************
;*
;* PUBLIC ROUTINE  do_shortline_pair   
;*
;* DESCRIPTION   = Draws a pair of lines
;*                                                                           
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        public  scan_next_plane
        public  device_comp_scan_done


        assumes es,nothing
        assumes ds,Data

cProc   do_shortline_pair,<PUBLIC,NEAR,NONWIN,NODATA>
cBegin
        test    loc_sd_fb,SD_DEVICE
        jz      itsa_bitmap                       ; this optimization not implem. for bitmaps

;/*
;** Compute and save the starting scan address (offset only, it's the device sel)
;*/

        mov     cx,loc_saved_y
        mov     ax,SCREEN_CBSCAN                  ;Faster to multiply on a 286
        mul     cx                                ;  than doing explicit shifts
        assert  dx,E,0
        sub     ax,SCREEN_CBSCAN
        mov     loc_scan_start.off,ax             ;Save address of scan
itsa_bitmap:

        public  next_scanline
next_scanline:
        dec     loc_saved_y                       ; Set Y for next scanline

        mov     ax,loc_saved_y_unfliped
        inc     ax
        mov     loc_saved_y_unfliped,ax

        mov     di,loc_current_rect
        cmp     ax,ss:[di].rcl_yTop.lo            ; top of clip-rect
        jae     this_scan_done                    ;!!! actually, this whole shortline is done

        lds     si,loc_right_sl_x                 ; DS:SI --> next right edge
        cld
        lodsw
        mov     dx,ax
        mov     loc_right_sl_x.lo,si

        lds     si,loc_left_sl_x                  ; DS:SI --> next left edge
        lodsw
        mov     loc_left_sl_x.lo,si

;/*
;** Check if the right-edge is actually left of the left-edge.
;*/

        cmp     ax,dx
        jg      points_misordered
        jl      points_order_okay
this_scan_done:
        jmp     do_scans_done
points_misordered:
        xchg    ax,dx
points_order_okay:

;/*
;** left edge is MAX ( AX , clip box left edge )
;*/

        mov     bx,ss:[di].rcl_xLeft.lo
        imax    ax,bx

;/*
;** right edge is MIN ( DX , clip box right edge )
;*/

        mov     cx,ss:[di].rcl_xRight.lo
        imin    dx,cx

;/*
;** Was the line completely outside the clip box?
;*/

        cmp     ax,dx
        jge     this_scan_done

        mov     loc_points.pts_x,ax
        mov     loc_points.pts_y,dx              ; "pts_y" is really 2nd X coord

;/*
;** Draw the scanline.  If get_fill_data returns with the carry set, then
;** there is no more work to do for this scanline, so go on to the next one.
;*/

        test    loc_sd_fb,SD_DEVICE
        jnz     device_comp_scan_done
        cCall   comp_scan_for_bitmap             ;Compute scanline for bitmap
device_comp_scan_done:

;/*
;**---------------------------------------------------------------------------
;** !!NOTE!!: There are two groups of parameters which are initialized by
;**           get_fill_data and comp_interval.
;**
;**               1) Parameters which are dependent on foreground/background:
;**                   their values are distinguished by colors and mixmode, so
;**                   we need two sets of them, ie.
;**                       loc_raster_op           loc_bk_raster_op
;**                       loc_transparency_mask   loc_bk_transparency_mask
;**                       loc_processors          loc_bk_processors
;**
;**               2) Parameters which are independent of foreground/background:
;**                   their values are dependent on the environment of operation,
;**                   such as device/bitmap, size of filled scanline, etc.
;**                   Therefore, both foreground and background will share the
;**                   same set of parameters. If the 2nd get_fill_data will
;**                   ever override the values intialized by the 1st call,
;**                   their values are still consistent. These parameters are:
;**                       loc_loop_control        loc_inner_loop_count
;**                       loc_first_mask          loc_last_mask
;**                       loc_clean_up
;**
;** Set parameters for foreground mixmode
;**---------------------------------------------------------------------------
;*/

        and     loc_some_flags,NOT (INDEX_BLNK_FORGRND OR INDEX_BLNK_BKGRND OR INDEX_BKGRD)
        cCall   get_fill_data                     ;Get data for filling foreground
        jnc     do_get_bk_fill_data     ;get_fill_data aborted for some reason
        or      loc_some_flags,INDEX_BLNK_FORGRND

;/*
;** Set parameters for background mixmode.
;*/

do_get_bk_fill_data:
        or      loc_some_flags,INDEX_BKGRD
        mov     ax,loc_raster_op
        xchg    ax,loc_bk_raster_op     ;Pretend the background is foreground
        mov     loc_raster_op,ax
        mov     ax,loc_transparency_mask
        mov     loc_bk_transparency_mask,ax
        cCall   get_fill_data           ;Get data for filling background
        mov     ax,loc_raster_op
        xchg    ax,loc_bk_raster_op
        mov     loc_raster_op,ax        ;Restore raster_op in the right place
        jnc     do_restore_parm         ;get_fill_data aborted for some reason
        test    loc_some_flags,INDEX_BLNK_FORGRND
        jnz     do_scans_done
        or      loc_some_flags,INDEX_BLNK_BKGRND

do_restore_parm:
        mov     ax,loc_transparency_mask
        xchg    ax,loc_bk_transparency_mask
        mov     loc_transparency_mask,ax        ;Restore mask in the right place
        mov     es,loc_scan_start.hi            ;This segment will stay unaltered
        assumes es,nothing
        call    comp_interval                   ;Get next interval to fill
        jc      do_scans_done                   ;No more points, exit
        mov     si,loc_loop_control             ;Set looping control (C3=>C2=>C1=>C0)
                                                ;  or C0 for mono and special EGA
scan_next_plane:
        sub     si,size cntrl_blk               ;--> Set next plane's data
        js      do_scans_done                   ;No more planes, get next interval
        add     di,loc_next_plane               ;DS:DI, ES:DI --> first byte, next plane
        cCall   draw_one_plane_of_scan
        jmp     short scan_next_plane


do_scans_done:                                  ; We're done with the current scanline
        test    loc_moore_flags,MF_SPECIAL_SOLID
        jnz     go_to_next_scan
        cmp     loc_clean_up,CU_NONE
        jz      go_to_next_scan
        cCall   scanline_reset_ega
go_to_next_scan:
        sub     loc_scan_start.off,SCREEN_CBSCAN
        dec     loc_scan_count
        jz      done_with_sl_pair
        jmp     next_scanline
done_with_sl_pair:
cEnd


;/***************************************************************************
;*
;* PUBLIC ROUTINE  draw_one_plane_of_scan 
;*
;* DESCRIPTION   = draws a single plane of a scan
;*
;*                 Registers Preserved:  
;*                       SI,DI,BP,DS,ES  
;*                 Registers Destroyed:  
;*                       AX,BX,CX,DX     
;*
;* INPUT         = BP --> PolyScanline stack frame                            
;*                 SI  =  index to current plane in array of processor blocks 
;*                 DI --> starting address of scan                            
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   draw_one_plane_of_scan,<PUBLIC,NEAR,NONWIN,NODATA>
cBegin
        test    loc_clean_up,CU_PLANE_SEL         ;EGA plane selection needed?
        jz      dops_do_first                     ;None needed
        mov     dx,EGA_BASE + SEQ_DATA
        mov     al,loc_bk_processors[si].plane_map_mask ; Assume no foregrnd
        test    loc_some_flags,INDEX_BLNK_FORGRND       ; Is it true?
        jnz     @F                                      ; yes, use bkgrnd info
        mov     al,loc_processors[si].plane_map_mask    ; no, must use foregrnd
@@:
        out     dx,al
        shr     al,1

        cmp     al,100b                           ;Set 'C' if not C3
        adc     al,-1                             ;Sub 1 only if C3

        mov     ah,al
        mov     al,GRAF_READ_MAP
        mov     dl,GRAF_ADDR
        out16   dx,ax

dops_do_first:
        test    loc_some_flags,INDEX_BLNK_FORGRND
        jnz     dops_bk_do_first
        test    loc_processors[si].nop_flag,NF_NOP  ;it is worth checking here
        jnz     dops_bk_do_first                    ;ROP is a nop, skip it
        push    di                                  ;Have to save starting address
        mov     bl,loc_processors[si].pattern
        mov     bh,bl
        mov     ax,loc_first_mask
        or      ax,ax
        jz      dops_do_inner
        and     ax,loc_transparency_mask
        mov     cx,1
        cCall   loc_processors[si].first_last_proc

dops_do_inner:
        mov     cx,loc_inner_loop_count              ;If no inner loop, skip it
        jcxz    dops_do_last
        mov     ax,loc_transparency_mask
        cCall   loc_processors[si].inner_loop_proc

dops_do_last:
        mov     ax,loc_last_mask
        or      ax,ax
        jz      dops_do_foregrnd_end
        and     ax,loc_transparency_mask
        mov     cx,1
        cCall   loc_processors[si].first_last_proc

dops_do_foregrnd_end:
        pop     di

;/*
;** At this point, there is no need to do the plane selection (if needed).
;** If there is no foreground job, EGA was already plane-selected.
;** Otherwise, although EGA was plane-selected with the information in
;** foreground structure, it is identical for background plane.
;*/

dops_bk_do_first:
        test    loc_some_flags,INDEX_BLNK_BKGRND
        jnz     dops_exit
        test    loc_bk_processors[si].nop_flag,NF_NOP
        jnz     dops_exit                        ;ROP is a nop, skip it
        push    di                               ;Have to save starting address
        mov     bl,loc_bk_processors[si].pattern
        mov     bh,bl
        mov     ax,loc_first_mask
        or      ax,ax
        jz      dops_bk_do_inner
        and     ax,loc_bk_transparency_mask
        mov     cx,1
        cCall   loc_bk_processors[si].first_last_proc

dops_bk_do_inner:
        mov     cx,loc_inner_loop_count          ;If no inner loop, skip it
        jcxz    dops_bk_do_last
        mov     ax,loc_bk_transparency_mask
        cCall   loc_bk_processors[si].inner_loop_proc

dops_bk_do_last:
        mov     ax,loc_last_mask
        or      ax,ax
        jz      dops_restore_pointer
        and     ax,loc_bk_transparency_mask
        mov     cx,1
        cCall   loc_bk_processors[si].first_last_proc

dops_restore_pointer:
        pop     di

dops_exit:
cEnd

;/*
;** SLM keeps choking because scanline.asm is too big, so...
;*/
        include pscanln.inc                       ; all the subroutines

sEnd    Code
end
