;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = 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 8514.inc
        include 8514mem.inc
        include cmplcode.inc
        include scanline.inc
        include assert.mac
        include njmp.mac
        .list

        ??_out  scanline
UsingLineSeg = 0

        errcode <BITMAP_NOT_SELECTED>

        externA  DOSHUGEINCR

        externFP FSRSemCheck

sBegin  PtrData
        externB screen_busy             ; cursor exclusion flag
sEnd    PtrData

sBegin Data
        externB ddcInit
        externD pfnDefRectVisible
        externW PatCacheX               ; x origin of board-cached pattern
        externW PatCacheY               ; y origin of board-cached pattern
        externW PatCacheXExt            ; width of board-cached pattern
        externW PatCacheYExt            ; height of board-cached pattern
        externW hwFlags                 ; hw configuration flags
        externB semDriver
        externD idBrushCache
        externW selCompileCode
sEnd   Data

        externFP far_exclude
        externFP far_unexclude
        externFP far_enumerate_clip_rects
        externFP far_get_clip_rects
        externFP far_cache_the_pattern


sBegin  FarCode
        externNP PropagateSysClrChange
sEnd    FarCode

sBegin  Code
        assumes cs,Code

        externB FarhwMixes                           ; rop to hw rop translate table

        externW CodeData
        externW MyCmplCodeData
        externW MyPtrCodeData

        externNP MakeBrushValid
        externNP do_sl_corr             ;A "common" routine found in
                                        ;   SCANCORR.ASM

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

define_frame    PolyScanline,FAR
cBegin
        fw_zero <ds,es>

        cld
        mov     ds,CodeData
        assumes ds,Data

;/*
;** This call 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     rc,1                    ; be an optimist

        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   far_enumerate_clip_rects,<si,myclip,CodeOFFSET do_sl_corr>
        jcxz    scanline_correlate_done
        mov     rc,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

; We are drawing the primitive for sure:

        mov     loc_moore_flags,0       ; initialize all flags to 0

; We are supposed to be drawing, but if there are no clip rectangles then
; there is nothing to draw into! Also, doing this here lets us test a bit
; later on rather than loading DS:SI with the DDC twice.

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

;/*
;** Up front let's check out the operation to see if it can be considered a
;** no-op. If it is, there is no sense in continuing with the operation.
;** We check for the case where both the fg and bg mixes are LeaveAlone
;** in which case there is nothing to be done. The other case where a particular
;** scan's transparency mask is all 0s or all 1s and the bg mix is ROP_D or the
;** fg mix is ROP_D respectively, is sufficiently pathologic that we will ignore
;** it here (but might not ignore it down at the bottom level where we actually
;** do the output).
;*/

        mov     dx,word ptr [si].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     @F
        cmp     al,ROP_D                        ;is background LeaveAlone?
        je      scanline_short_exit     ; yes -- no work to be done...
@@:

;/*
;** The operation will really result in some output (as long as some of the
;** scanlines are visible within the clip rects) so it is now wise to go
;** ahead and realize a brush.
;*/

        mov     ax,[si].ddc_iSysClr
        cmp     ax,ddcInit.ddc_iSysClr
        je      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 parameters
        jns     scanline_brush_ok                ;Error logged by MakeBrushValid
        jmp     get_rc_and_leave
scanline_brush_ok:

;/*
;** Now that the brush is realized we can move the transparency masks up on
;** to the local frame for more efficient access from the doer routines.
;*/

        mov     ax,ss
        mov     es,ax
        assumes es,nothing
        lea     di,loc_trans_mask       ; ES:DI is -> local brush mask
        mov     ax,si
        lea     si,[si].ddc_pa.pa_abMask; DS:SI is -> ddc brush mask
        cld                             ; ensure working up in address
        movsw
        movsw
        movsw
        movsw
        .errnz  SIZE_PATTERN - 8
        mov     si,ax                   ; DS:SI restored to point at DDC

;/*
;** Set up the indirect pointer to the scanline routines at the bottom level.
;*/
        public  psl_set_pointers
psl_set_pointers:

        mov     ax,CodeOFFSET ScanlineDeviceInit; assume to the device
        mov     bx,CodeOFFSET ScanlineDeviceDoer; assume to the device
        mov     cx,CodeOFFSET ScanlineDeviceDeinit; assume to the device
        test    [si].ddc_fb,DDC_DEVICE  ; is the destination the device ?
        jnz     @F                      ; yes...
        mov     di,[si].ddc_npsd        ; get the -> the target surface defn.
        mov     ax,CodeOFFSET ScanlineMonoInit; assume mono target bitmap
        mov     bx,CodeOFFSET ScanlineMonoDoer; assume mono target bitmap
        mov     cx,CodeOFFSET ScanlineMonoDeinit; assume mono target bitmap
        test    [di].sd_fb,SD_COLOR     ; is that right ?
        jz      @F                      ; yes...
        mov     ax,CodeOFFSET ScanlineColorInit; color bitmap init'er
        mov     bx,CodeOFFSET ScanlineColorDoer; color bitmap do'er
        mov     cx,CodeOFFSET ScanlineColorDeinit; color bitmap do'er
@@:     mov     loc_scanline_init.off,ax; stuff init'er ->
        mov     loc_scanline_doer.off,bx; stuff do'er ->
        mov     loc_scanline_deinit.off,cx; stuff deinit'er ->
        mov     ax,CodeBASE
        mov     loc_scanline_init.sel,ax
        mov     loc_scanline_doer.sel,ax
        mov     loc_scanline_deinit.sel,ax

;/*
;** Do operation dependent initializations.
;*/

        mov     di,[si].ddc_npsd        ; get surface definition from ddc
        mov     cx,[di].sd_cy           ; save for flipping Y coord
        mov     loc_bm_height,cx        ; need this before call to init'er

        public  psl_call_init
psl_call_init:
        call    loc_scanline_init


        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
        call    far_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 returned 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.
;*/


        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.
;*/

        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

        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.  The pointer in loc_scanline_doer is vectored to point
;** to one of three bottom level do'ers depending on whether or not the
;** target is a mono bitmap, color bitmap, or the device.
;*/

        public  psl_call_doer
psl_call_doer:
        call    loc_scanline_doer       ; call indirect to do'er routine

do_scans_done:

        dec     loc_scan_count
        jz      done_with_sl_pair
        jmp     next_scanline

        public  done_with_sl_pair
done_with_sl_pair:
        dec     loc_sl_count
        jz      done_with_scans_data
        jmp     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:

        public  psl_call_deinit
psl_call_deinit:
        call    loc_scanline_deinit     ; deinitialization operations
get_rc_and_leave:
        mov     ax,rc                   ; Make AX nonzero to show success
scanline_get_out:
        cwd
        fw_zero <cx,ds,es>
cEnd

        externW MyPtrCodeData
        externW CodeData

;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlinePatternPreprocessing
;*
;* DESCRIPTION   =  Preprocess pattern -- set up local flags, perhaps cache 
;*                  pattern on board.                                       
;*
;*                  Registers Preserved:                 
;*                        SI,BP,DS                       
;*                  Registers Destroyed:                 
;*                        AX,BX,CX,DX,DI,ES 
;*
;* INPUT         = DS:SI   ->      DDC               
;*                                                  
;* OUTPUT        = 'C'=0 if destination is a device. 
;*                 'C'=1 if destination is a bitmap. 
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

CMDPAT  =       (\
                 CMD_C_HRECT+CMD_BYTE_LO+CMD_BYTE+CMD_FV_VAR+CMD_DY+CMD_DX+\
                 CMD_MA_ACCESS+CMD_PA_ONE+CMD_RW_W\
                )

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlinePatternPreprocessing,FAR
cBegin  <nogen>

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

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

;/*
;** Won't have to cache a pattern if the pattern is solid.
;*/

        mov     al,[si].ddc_pa.pa_fb    ; get copy of brush flags
        mov     loc_brush_accel,al      ; and save them
        test    al,PA_SINGLE_CLR        ; solid pattern ?
        jz      @F                      ; no...
        mov     al,[si].ddc_pa.pa_ba.ba_ipcBack.ipc_bClr; fetch bg color
        mov     ah,[si].ddc_pa.pa_ba.ba_ipc.ipc_bClr; fetch fg color
        mov     loc_both_colors,ax      ; cache locally
spp_exit_relay:
        jmp     spp_exit                ; will use filled rect instead...
@@:

;/*
;** Can bail out now if the target is not the device.
;*/

        test    [si].ddc_fb,DDC_DEVICE
        jz      spp_exit_relay          ; the destination is a bitmap...

;/*
;** The pattern is not solid and the destination is the device -- we need
;** to latch in a local variable the y address of the pattern cache on the
;** board. This is because the doer routine does not have convenient access
;** to the data segment but always has access to the stack segment.
;*/

        mov     ax,PatCacheY
        mov     loc_PatCacheY,ax

;/*
;** If the ddc's  pattern is already stored in the cache, then we don't
;** have to reload it.  We don't have to go critical to test the id since
;** we already on the driver semaphore.
;*/

        mov     ax,[si].ddc_pa.pa_idBrush.lo
        cmp     ax,idBrushCache.lo
        jne     spp_must_load_user_pattern
        mov     ax,[si].ddc_pa.pa_idBrush.hi
        cmp     ax,idBrushCache.hi
        je      spp_exit_relay
spp_must_load_user_pattern:


;/*
;** Will really be realizing the pattern on the board. First prevent the
;** cursor from coming in at interrupt time and killing our register state.
;*/

        mov     es,MyPtrCodeData
        assumes es,PtrData
        GrabScreenEs  scanline1         ; don't let cursor kill us
        assumes es,nothing

        call    far_cache_the_pattern
        mov     ax,[si].ddc_pa.pa_idBrush.lo
        mov     idBrushCache.lo,ax
        mov     ax,[si].ddc_pa.pa_idBrush.hi
        mov     idBrushCache.hi,ax

        mov     es,MyPtrCodeData
        assumes es,PtrData
        ReleaseScreenEs  scanline1       ; cursor can do its thing
        assumes es,nothing

spp_exit:
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlineDeviceInit
;*
;* DESCRIPTION   =  Initialize for polyscanlines to the device 
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineDeviceInit,FAR
cBegin  <nogen>

;/*
;** Brush is now valid in local cached brush arrays. We check here that
;** the cached brush on the board is valid and if it isn't we rerealize
;** the brush on the board.
;*/

        call    ScanlinePatternPreprocessing

;/*
;** We need to exclude the cursor. (Just exclude to max bitmap bounds for now)
;*/

        les     di,pScansData           ; ES:DI --> ScanData structure
        assumes es,nothing

        mov     ax,es:[di].sd_rclBound.rcl_xLeft.lo     ; left x
        mov     si,es:[di].sd_rclBound.rcl_xRight.lo    ; right x
        mov     bx,es:[di].sd_rclBound.rcl_yTop.lo      ; top y
        mov     di,es:[di].sd_rclBound.rcl_yBottom.lo   ; bottom y

;/*
;** Clip all 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
        mov     dx,bx                   ; top must be here in DX
        neg     di
        add     di,loc_bm_height

        call    far_exclude
        mov     ds,CodeData                           ; restore DS:SI (exclude destroyed)
        assumes ds,Data
        mov     si,arg_lp_ddc.lo        ; DS:[SI] --> DDC
        ddc?    si

        mov     es,MyPtrCodeData
        assumes es,PtrData
        GrabScreenEs  scanline2         ; don't let cursor operations kill us
        assumes es,nothing

;/*
;** All the shortlines are clipped in the generic code below. If this operation
;** is to the device we need not set the clip rectangle for each new clip rect
;** gotten. Instead we set up the scissor rectangle on the board to the max
;** scissor rectangle and never worry about it again. Also, several other
;** board registers may be initialized here never to be touched again.
;*/

        WaitQ   8

;/*
;** set up the scissor rectangle
;*/

        outwQ   XMIN,(0+XMIN_2DECODE)   ; min x
        outwQ   XMAX,(1023+XMAX_2DECODE); max x
        outwQ   YMIN,(0+YMIN_2DECODE)   ; min y
        outwQ   YMAX,(1023+YMAX_2DECODE); max y

;/*
;** mode register may always specify pattern source to be pattern registers
;*/

        outwQ   MODE,(MODE_2DECODE+MD_PS_PATT); pattern source is always pat reg

;/*
;** we will always load the color registers just in case the pattern is solid
;*/

        outbQ   COLOR_0,<byte ptr loc_both_colors[0]> ; set bg color
        outbQ   COLOR_1,<byte ptr loc_both_colors[1]> ; set fg color

        outwQ   LY,0                    ; height of any scan is always 1

        WaitQ   4

;/*
;** set up the mix mode for the fg and bg mixes
;*/

        mov     ax,(FUNC_2OP_COPY+(FUNC_2OP_COPY shl 8)); assume pattern brush
        mov     cx,CMD_C_CRECT+CMD_FV_FIX+CMD_DY+CMD_DX+CMD_MA_ACCESS+\
                   CMD_PA_FOUR+CMD_RW_W ; assume solid pattern
        test    loc_brush_accel,PA_SINGLE_CLR; solid brush ?
        jz      @F                      ; no -- its pattern...
        mov     ax,(FUNC_2OP_COL1+(FUNC_2OP_COL0 shl 8)); 2nd op src color reg
        mov     cx,CMD_C_HRECT+CMD_FV_FIX+CMD_DY+CMD_DX+CMD_MA_ACCESS+\
                   CMD_PA_FOUR+CMD_RW_W ; assume solid pattern
@@:     mov     loc_dev_cmd,cx          ; store command to hw

        mov     bl,[si].ddc_pa.pa_ba.ba_bmix; fetch fg mix
        and     bx,000fh                ; zero-extend mix for index usage
        mov     cx,CodeBASE
        mov     es,cx
        assumes es,Code
        or      al,FarhwMixes[bx]                   ; combine mix and 2nd op src
        outbQ   FUNCTION_1,al           ; set fg function

        xchg    al,ah                   ; fetch 2nd op src
        mov     bl,[si].ddc_pa.pa_ba.ba_bkmix; fetch bk mix
        and     bx,000fh                ; zero-extend mix for index usage
        mov     cx,CodeBASE
        mov     es,cx
        assumes es,Code
        or      al,FarhwMixes[bx]                   ; combine mix and 2nd op src
        outbQ   FUNCTION_0,al           ; set bg function

;/*
;** make sure all planes are enabled for reading and writing:
;*/

        outbQ   WRITE_ENABLE,WRITE_ALL_PLANES
        outbQ   READ_ENABLE,READ_ALL_PLANES

        clc                             ; no errors from here
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlineDeviceDoer
;*
;* DESCRIPTION   = Display polyscanlines on the device.
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
 
        assumes ds,nothing
        assumes es,nothing

define_frame    ScanlineDeviceDoer,FAR
cBegin  <nogen>

;/*
;** Need to get the pattern, phased in x and y, and load the pattern registers.
;** (We don't actually have to phase the pattern in x since the hardware ends
;** up doing that very nicely for us).
;*/

        WaitQ   2

;/*
;** First phase in y:
;*/

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

;/*
;** Now do operation dependent stuff
;*/

        test    loc_brush_accel,PA_SINGLE_CLR; solid brush ?
        jz      sdd_pattern_scan        ; no -- do pattern scanline...

;/*
;** Output a solid scanline to the hardware
;*/

        WaitQ   4
        outwQ   Y0,bx                   ; y of current scanline
        outwQ   X0,loc_points.pts_x     ; left x of scanline
        neg     ax
        add     ax,loc_points.pts_y     ; (right x + 1) - left x = width
        dec     ax                      ; 0-based width of scanline
        outwQ   LX                      ; tell the hardware the width
        outwQ   CMD_FLAGS,loc_dev_cmd   ; send command to hardware

        jmp     short   sdd_exit

sdd_pattern_scan:

;/*
;** Output a patterned scanline to the hardware
;*/

        WaitQ   6
        add     di,loc_PatCacheY        ; top y of pattern cache
        outwQ   Y0,di                   ; y of source scanline
        outwQ   Y1,bx                   ; y of destination scanline
        outwQ   X0,loc_points.pts_x     ; left x of scanline
        outwQ   X1,ax                   ; left x of scanline
        neg     ax
        add     ax,loc_points.pts_y     ; (right x + 1) - left x = width
        dec     ax                      ; 0-based width of scanline
        outwQ   LX                      ; tell the hardware the width
        outwQ   CMD_FLAGS,loc_dev_cmd   ; send command to hardware

sdd_exit:
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlineDeviceDeinit   
;*
;* DESCRIPTION   =  Outgoing chores for polyscanlines to the device. 
;*
;* INPUT         = Stack frame as per PolyScanLine.
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineDeviceDeinit,FAR
cBegin  <nogen>

        mov     es,MyPtrCodeData
        assumes es,PtrData
        ReleaseScreenEs scanline2 ; allow cursor to do what it has to
        assumes es,nothing

        call    far_unexclude

        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlineMonoInit
;*
;* DESCRIPTION   =  Initialize for polyscanlines on a monochrome bitmap. 
;*
;* INPUT         = Stack frame as per PolyScanLine.
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,Data
        assumes es,nothing

define_frame    ScanlineMonoInit,FAR
cBegin  <nogen>
        clc                             ; never any error (yet)
        ret
cEnd    <nogen>


;/***************************************************************************
;*
;* PUBLIC ROUTINE   ScanlineDeviceDoer
;*
;* DESCRIPTION   = Display polyscanlines on a monochrome bitmap.
;*
;* INPUT         = Stack frame as per PolyScanLine. 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

define_frame    ScanlineMonoDoer,FAR
cBegin  <nogen>

;/*
;** 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.
;*/

        call    far ptr comp_scan                 ;Compute scanline, device data

        and     loc_some_flags,NOT (INDEX_BLNK_FORGRND OR INDEX_BLNK_BKGRND OR INDEX_BKGRD)
        call    far ptr get_fill_data             ;Get data for filling foreground
        jnc     scan_get_bk_fill_data     ;get_fill_data aborted for some reason
        or      loc_some_flags,INDEX_BLNK_FORGRND

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

scan_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
        call    far ptr 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     scan_restore_parm         ;get_fill_data aborted for some reason
        test    loc_some_flags,INDEX_BLNK_FORGRND
        njnz    scan_exit
        or      loc_some_flags,INDEX_BLNK_BKGRND

scan_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    far ptr comp_interval   ;Get next interval to fill
        njc     scan_exit               ;No more points, exit

        add     di,loc_next_plane       ;DS:DI, ES:DI --> first byte, next plane
        test    loc_some_flags,INDEX_BLNK_FORGRND
        jnz     scan_bk_do_first
        test    loc_processors.nop_flag,NF_NOP  ;it is worth checking here
        jnz     scan_bk_do_first                 ;ROP is a nop, skip it
        push    di                               ;Have to save starting address
        mov     bl,loc_processors.pattern
        mov     bh,bl
        mov     ax,loc_first_mask
        or      ax,ax
        jz      scan_do_inner
        and     ax,loc_transparency_mask
        mov     cx,1
        cCall   loc_processors.first_last_proc

scan_do_inner:
        mov     cx,loc_inner_loop_count ;If no inner loop, skip it
        jcxz    scan_do_last
        mov     ax,loc_transparency_mask
        cCall   loc_processors.inner_loop_proc

scan_do_last:
        mov     ax,loc_last_mask
        or      ax,ax
        jz      scan_do_foregrnd_end
        and     ax,loc_transparency_mask
        mov     cx,1
        cCall   loc_processors.first_last_proc

scan_do_foregrnd_end:
        pop     di

scan_bk_do_first:
        test    loc_some_flags,INDEX_BLNK_BKGRND
        jnz     scan_exit
        test    loc_bk_processors.nop_flag,NF_NOP
        jnz     scan_exit                        ;ROP is a nop, skip it
        mov     bl,loc_bk_processors.pattern
        mov     bh,bl
        mov     ax,loc_first_mask
        or      ax,ax
        jz      scan_bk_do_inner
        and     ax,loc_bk_transparency_mask
        mov     cx,1
        cCall   loc_bk_processors.first_last_proc

scan_bk_do_inner:
        mov     cx,loc_inner_loop_count ;If no inner loop, skip it
        jcxz    scan_bk_do_last
        mov     ax,loc_bk_transparency_mask
        cCall   loc_bk_processors.inner_loop_proc

scan_bk_do_last:
        mov     ax,loc_last_mask
        or      ax,ax
        jz      scan_exit
        and     ax,loc_bk_transparency_mask
        mov     cx,1
        cCall   loc_bk_processors.first_last_proc

scan_exit:
        ret

cEnd    <nogen>

;/*
;** Too appease SLM, the rest of this file has been moved to SCANLN2.ASM
;*/

        include scanln2.asm

sEnd    Code

end
