;*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 =  POLYLINE.ASM
;*
;* DESCRIPTIVE NAME =  Polyline drawing device driver. 
;*
;*
;* VERSION      V2.0
;*
;* DATE         03/03/87
;*
;* DESCRIPTION  
;*              
;*   Given a set of points, draw a set of polylines connecting adjoining
;*   points.  Depending on whether we are writing to the EGA or a bitmap
;*   we initialize as necessary.  Solid and styled lines are handled.
;*   Small (<= 64k bytes) monochrome and color bitmaps and huge monochrome
;*   and color bitmaps are all supported.  A run length slice algorithm is
;*   used to determine the pixels used to draw each line.  The algorithm is
;*   explained later on.
;*  
;*   The line drawing code is slightly different depending on whether
;*   we are drawing solid lines to the EGA, solid lines to a bitmap, or
;*   styled lines to either the EGA or a memory bitmap.  For the sake
;*   of speed, a different set of line drawing routines is called
;*   for each case.
;*  
;*   There are sixteen raster operations (sets of logical operations) performed
;*   on the data written out.  When writing to the EGA there are four of
;*   these operations which take two passes of EGA memory.  In each of these
;*   cases the first passes sets the necessary bits in the necessary planes
;*   to ones or zeros explicitly.  The second pass then XORs the necessary
;*   bit in the necessary planes.  The other twelve raster operations can
;*   be done in one pass of EGA memory.  All raster operations are done in
;*   one pass of memory for bitmaps.  Depending on the raster operation and
;*   the color of the pen, it is easily determined whether we set bits to
;*   zeros, set bits to ones, invert bits or do nothing.  Bitmaps are written
;*   to one plane at a time.  In the case of a mono bitmap, only one plane is
;*   written to.
;*  
;*   Huge bitmaps are stored on a scan line basis.  For example, if we have
;*   three color planes, the the bitmap is stored as a red scan line, a green
;*   scan line, a blue scan line, a red scan line, and so on.  Because of this
;*   format, there will most likely be some left over bytes at the end of the
;*   segment.  These will have to be accounted for when crossing segment
;*   boundaries.
;*  
;*   Lines are drawn from left to right.  So if a line moves from right
;*   to left, the endpoints are swapped and the line is drawn from left to
;*   right.
;*  -----------------------------------------------------------------------;
;*  -----------------------------------------------------------------------;
;*                     Our Favorite Line Drawing Algorithm
;*  
;*   I derive here the Run Length Algorithm, originally due to Bresenham.
;*   (NATO ASI Series, Volume F17, Fundamental Algorithms for Computer Graphics,
;*   Springer-Verlag, Berlin Heidelberg 1985)
;*  
;*   Suppose we want to draw a line from integer points (0,0) to (dx,dy), and we
;*   are given that:
;*  
;*       0 < dx < 65536  and  0 <= dy <= dx/2.                               (1)
;*  
;*   It happens that it is useful to have dx/dy >= 2. This allows a faster
;*   algorithm and prevents overflow in some of the calculations which will
;*   involve 2 dy.
;*  
;*   Lines in other directions can be handled by flipping them in various
;*   directions to get them into the "lower half octant" of (1).  We will always
;*   assume that lines will be drawn left to right.  This gets rid of several
;*   cases of inner loop code.  Simply exchanging the end points of the line can
;*   insure left-to-rightness.  If |slope| > 1, the line is "y-major", and we will
;*   exchange the x and y coordinates to make it x-major.  If we then have
;*   slope < 0, we negate the y coordinate.  And finally, if slope > 1/2, we
;*   subtract the y coordinates from the x coordinates. This has the effect of
;*   reflecting the coordinates through the line of slope 1/2.  I will call this
;*   a "half flip". Note that the diagonal gets mapped into the x axis after a
;*   half flip.
;*  
;*   We must record each step of the transformation that we have done so that we
;*   will be able to do the rendering in the proper direction after calculating
;*   what pattern of pixels should be drawn.  We will also need to pay special
;*   attention to lines that have undergone a flip in the vertical direction,
;*   either through the x axis or the half flip.  I will call these lines
;*   "flipped".  If the line has been reflected by both of these, I will count it
;*   as not flipped.
;*  
;*   The first thing to decide is what scheme we will use to determine if a given
;*   pixel should be turned on, i.e. our "metric".  Our scheme is as follows:
;*   First, imagine that the real geometric line has been drawn from (0,0) to
;*   (dx,dy).  Second, draw a vertical line segment of length one centered on the
;*   pixel in question.  If the geometric line intersects the vertical line
;*   through our pixel, we will turn the pixel on, otherwise it stays off.  If the
;*   geometric line exactly hits the upper end of our vertical line we define this
;*   to be an intersection, but not if it exactly hits the bottom.  I.e. 1/2 gets
;*   rounded down.
;*  
;*   We have rotated and flipped some lines to get them under the restrictions of
;*   (1).  For consistency, we should use the same "metric" described above to
;*   decide what pixels should be on for any line BEFORE it is flipped around
;*   for our calculation.  Having a consistent metric this way will let our lines
;*   blend nicely with our curves. For lines that are "y-major", i.e. have
;*   |slope| > 1, the consistent metric would use horizontal lines through each
;*   pixel, and decide ties by rounding to the left.  If any line has undergone a
;*   vertical flip to fit (1), we'll need to know that, since the metric would
;*   then require that 1/2 be rounded down.
;*  
;*   I assume that all the pixels are arranged on a Cartesian coordinate system,
;*   and that the pixels live at the points where both x and y are integers.  I
;*   will call the lines of constant integer y coordinate "scans" or "scanlines".
;*   The line that we draw under restriction (1) will consist of varying numbers
;*   of pixels on successive scans, for example:
;*  
;*                        *****
;*                  ******
;*             *****
;*       ******
;*  
;*   Due to our choice of metric, we will never have one pixel turned on directly
;*   above another that's turned on.  We will also never have a gap, i.e. there
;*   will be exactly one pixel turned on for each integer x between 0 and dx. All
;*   that remains is to decide how many pixels should be turned on for each scan.
;*   Note that if we have done a half flip on our original line then the scans
;*   pictured above will actually run along a diagonal.
;*  
;*   Consider an arbitrary scan corresponding to the integer y.  Suppose that our
;*   geometric line intersects the "half scan" below this line, i.e. the
;*   horizontal line at y-1/2, at some rational number x.  The first pixel that
;*   should be turned on for this scan is given by:
;*  
;*       x     = floor(x) + 1                                            (2)
;*        first
;*  
;*   The floor()+1 function is important here.  If the geometric line intersects
;*   the half scan between two integers, the upper integer is the first pixel we
;*   should turn on.  If the geometric line intersects the half scan exactly at an
;*   integer, then we are at the very end of one of our vertical test lines and
;*   according to our metric the pixel in the scan below is the one to light.
;*   Therefore the next integer up is again the first pixel.  This is just
;*   floor(x)+1.
;*  
;*   Suppose we define the inverse slope to be:
;*  
;*       s = dx/dy,                                                      (3)
;*  
;*   then our geometric line will intersect the next half scan at x+s.  The last
;*   pixel we should light on this scan is therefore:
;*  
;*       x    = floor(x + s)                                             (4)
;*        last
;*  
;*   If the geometric line intersects the half scan between two integers, the
;*   lower one is the last pixel. If the geometric line intersects the half scan
;*   exactly at some integer, this is a tie case which our metric decides to put
;*   in this scan.  This is the floor function.
;*  
;*   The number of pixels to light on this scan is therefore:
;*  
;*       c  = x    - x     + 1 = floor(x + s) - floor(x),                (5)
;*        y    last   first
;*  
;*   The integer part of x passes out of the floor function and cancels out. If we
;*   define the fractional part:
;*  
;*       f = frac(x),                                                    (6)
;*  
;*   we then have:
;*  
;*       c  = floor(f + s) - floor(f)
;*        y
;*                                                                       (7)
;*          = floor(f + s)
;*  
;*   Since we know that the intersection of the geometric line with the half scan
;*   below y is at:
;*  
;*       x = (y - 1/2) s,                                                (8)
;*  
;*   we know that the f in (7) is given by:
;*  
;*       f = frac(ys - s/2).                                             (9)
;*  
;*   To handle the cases of flipped lines, we could just recalculate (2) and (4)
;*   with the consideration that the metric causes us to round up instead of down.
;*   This works, but leads to different formulas for the flipped and non-flipped
;*   cases, which would add complexity to our implementation. Instead we use a
;*   simplifying trick.  The business of whether the metric rounds up or down only
;*   concerns us in those cases where the geometric line exactly hits end of one
;*   of our vertical test lines.  This happens when the intersection of the
;*   geometric line with a half scan occurs exactly at an integer.  Suppose now
;*   that, if the line is a flipped one, we don't use the geometric line, but
;*   instead use the geometric line shifted left by 1/(2 dy). The shifted
;*   geometric line would simply intersect the test line in the scan above.  This
;*   is the result we would want from rounding up.  Now suppose that the
;*   intersection of the original geometric line with the halfscan is not exactly
;*   an integer.  Then it must be some number of 1/(2 dy) units above an integer.
;*   The shifted line will have an intersection 1/(2 dy) to the left. At worst,
;*   this would now be at the integer.  But if we use the usual metric to just
;*   round down, this will not change what pixel gets displayed.
;*  
;*   Thus, we can calculate everything about a round-up metric by using the usual
;*   round down metric with a shifted geometric line.  Equation (7) remains valid
;*   except that we need to redefine f from equation (9):
;*  
;*       f = frac(ys - s/2 + f ),                                        (10)
;*                            0
;*  
;*   where:
;*            { -1/(2 dy)  if the line is flipped
;*       f  = {                                                          (11)
;*        0   {     0      otherwise
;*  
;*   The behavior of c in equation (7), as a function of f, is very easy to
;*   characterize.  When f is smaller than 1-frac(s), it cannot push the
;*   argument of the floor function above the next integer.  The result is
;*   therefore floor(s).  As soon as f equals or exceeds 1-frac(s), the result
;*   becomes floor(s)+1.  If we define:
;*  
;*       m = floor(s),                                                   (12)
;*  
;*   we can say:
;*  
;*            {  m   if  f < 1 - frac(s)
;*       c  = {                                                          (13)
;*        y   { m+1  if  f >= 1 - frac(s)
;*  
;*   As an implementation aid, we will want to define:
;*  
;*       f' = f + frac(s) - 1.                                           (14)
;*  
;*   So that (13) becomes:
;*  
;*            {  m   if  f' < 0
;*       c  = {                                                          (15)
;*        y   { m+1  if  f' >= 0
;*  
;*   The first and last scans of our line will not, in general, have this many
;*   pixels since the geometric line terminates at the end points and doesn't
;*   therefore penetrate the whole scan.  Relation (15) tells us about all the
;*   scans in between the first and last scans.  We don't really want to
;*   calculate (10) for every scan, instead we can calculate it for the second
;*   scan and just increment it to get the rest.
;*  
;*       f  = frac(s/2 + f )
;*        1               0
;*                                                                       (16)
;*       f   = frac(s + f )
;*        N+1            N
;*  
;*   Our f' can be advanced for each scan nearly as easily as f, so instead we
;*   could use:
;*  
;*       f' = frac(s/2 + f ) + frac(s) - 1
;*        1               0
;*  
;*       f'  = frac((N+1)s - s/2 + f ) + frac(s) - 1
;*        N+1                       0
;*  
;*           = frac(s + Ns - s/2 + f ) + frac(s) - 1
;*                                  0
;*                                                                       (17)
;*           = frac(s + frac(Ns - s/2 + f )) + frac(s) - 1
;*                                       0
;*  
;*           = frac(s + f' - frac(s) + 1) + frac(s) - 1
;*                       N
;*  
;*           = frac(f') + frac(s) - 1
;*                   N
;*  
;*   And now we will calculate how many pixels to display on the first and last
;*   scans. We are given that the first pixel is at (0,0). The last pixel on the
;*   first scan is at:
;*  
;*       x    = floor(f + s/2)                                           (18)
;*        last         0
;*  
;*   The number of pixels on the first scan is therefore:
;*  
;*       c  = floor(f + s/2) + 1                                         (19)
;*        0          0
;*  
;*   As in (2), the first pixel on the last scan is at:
;*  
;*       x     = floor(dx + f - s/2) + 1                                 (20)
;*        first              0
;*  
;*   The number of pixels on the last scan is therefore:
;*  
;*       c  = dx - (floor(dx + f - s/2) + 1) + 1
;*        dy                    0
;*  
;*          = -floor(f - s/2)                                            (21)
;*                    0
;*  
;*          = ceil(s/2 - f )
;*                        0
;*  
;*   This        much wraps up the mathematical problem, if we are interested in
;*   drawing the whole line.  But in many cases we will want to clip the line.  I
;*   will assume that some clipping calculations have occurred before our line
;*   drawing routine is called.  I will assume that our routine is called with an
;*   interval, which is a subset of [0,dx], in which the line is to be drawn.  In
;*   order for external routines to be able to calculate this interval they will
;*   need to know our metric.
;*  
;*   Assume that the inclusive interval we are given is:
;*  
;*       [x , x ]  where  0 <= x <= x <= dx.                             (22)
;*         a   b                a    b
;*  
;*   Then the first and last scans we will draw on will be:
;*  
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        a          a   0
;*                                                                       (23)
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        b          b   0
;*  
;*   The number of pixels to draw on the first and last scans are:
;*  
;*       c  = floor(s(y + 1/2) + f ) - x + 1
;*        a            a          0     a
;*                                                                       (24)
;*       c  = x  - floor(s(y - 1/2) + f )
;*        b    b            b          0
;*  
;*   The fractional part of the intersection with the second scan is:
;*  
;*       f  = frac(s(y + 1) + f )                                        (25)
;*        1           a        0
;*  
;*   so that:
;*  
;*       f' = frac(s(y + 1/2) + f ) + frac(s) - 1                        (26)
;*        1           a          0
;*  
;*   The common subexpression in (24) and (26) is very convenient and will save us
;*   some computation.  Surprisingly, the argument to the ceil function in (23) is
;*   also intimately related to the subexpression.  Whereas it might look like it
;*   takes two multiplies and two divides to calculate (23), (24), and (26) for
;*   the first clipped scan, we can actually get by with only one multiply and two
;*   divides.  Here's how we do it.  Note that the argument to the ceil function
;*   of (23) is always a multiple of 1/(2 dx).  In much the same way that we have
;*   handled flipped lines, we can subtract 1/(2 dx) from the argument to turn the
;*   ceil function into a floor function.  Thus:
;*  
;*       y  = ceil((x - f )/s + 1/2) - 1
;*        a          a   0
;*  
;*          = floor((x - f )/s + 1/2 - 1/(2 dx))                         (27)
;*                    a   0
;*  
;*          = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))
;*                         a         0
;*  
;*   When we do the division here, we will get back an integer quotient which
;*   is the y value we are looking for.  But we also get a remainder rm, which
;*   will be somewhere between 0 and 2 dx.  Putting the division back together
;*   we get:
;*  
;*       2 dx y  + rm = 2 dy x  - 2 dy f  + dx - 1                       (28)
;*             a              a         0
;*  
;*   Let's now go back to the subexpression we are interested in:
;*  
;*       s(y + 1/2) + f  = (2 dx y  + dx + 2 dy f ) / (2 dy)
;*          a          0          a              0
;*  
;*             = (2 dy x  + 2 dx - (rm + 1)) / (2 dy)
;*                      a                                                (29)
;*  
;*                    2 dx - (rm + 1)            (rm + 1)
;*             = x  + --------------- = x  + s - --------
;*                a        2 dy          a         2 dy
;*  
;*   Remember that rm is positive, but strictly less than 2 dx.  What this tells
;*   us is that the intersection of the geometric line with the next half scan
;*   must be above, but less than s above, the x point we are clipping to.  This
;*   makes perfect sense.  Thus, after computing our starting y scan, it takes
;*   only the one division in the final expression (29) to compute:
;*  
;*       c  = floor(s(y + 1/2) + f ) - x + 1
;*        a            a          0     a
;*                                                                       (30)
;*                       rm + 1
;*          = floor( s - ------ ) + 1,
;*                        2 dy
;*  
;*   and:
;*  
;*       f' = frac(s(y + 1/2) + f ) + frac(s) - 1
;*        1           a          0
;*                                                                       (31)
;*                      rm + 1
;*          = frac( s - ------ ) + frac(s) - 1.
;*                       2 dy
;*  
;*   Of course, an identical relationship exists for the ending scan, so that if
;*   we have the remainder rm' we can compute:
;*  
;*       c  = x  - floor(s(y - 1/2) + f )
;*        b    b            b          0
;*                                                                       (32)
;*                      rm'+ 1           rm'+ 1
;*          = - floor(- ------ ) = ceil( ------ )
;*                       2 dy             2 dy
;*  
;*   And we again save a multiply.  Of course, I anticipate that the vast majority
;*   of the time we will not clip, or will only clip a pixel off of one end.  We
;*   will have special case code to handle those times when we are only removing a
;*   few pixels from the first or last scan.
;*  
;*   We will implement all the above using a fractional representation, i.e. all
;*   rational numbers will be kept as multiples of 1/(2 dy).  This minimizes the
;*   computation involved.  I have not made this explicit since it obscures many
;*   otherwise very simple equations and derivations.
;*              
;*              
;*              
;* FUNCTIONS    do_polylines
;*                          
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   03/03/87                     Written by Kent Settle
;*   04/08/87                     Kent Settle [kentse] Modified to draw all
;*                                lines moving right.
;*   04/30/87                     Kent Settle [kentse] Added huge bitmap handling.
;*   10/23/87                     Wesley O. Rupel [wesleyr] Optimized, Added
;*                                FireWalls, removed pd_screen_sel
;*   05/07/88                     Charles Whitmer [chuckwh] Rewrote from scratch.
;*                                Moved half of the clipping work out of
;*                                polyline_workhorse_routine.
;*   05/20/88                     Charles Whitmer [chuckwh] Derived all the
;*                                above for the case of clipped lines, which
;*                                Bresenham neglected to cover in his paper.
;*   06/20/88                     Bob Grudem [bobgru] Implemented the "max"
;*                                metric for line styling.
;*   06/18/89                     Lee A. Newberg [leen] Supports DisjointLines.
;*   05/25/91            PSM04063 Paul R. King [PAULKING] Added the fComFlags
;*                                parameter to fix PTR #PSM04063
;*
;*****************************************************************************/

        .386

        .xlist

INCL_GRE_LINES          equ     1
INCL_DDIPATHS           equ     1
INCL_GPIPRIMITIVES      equ     1

        include pmgre.inc

DINCL_ENABLE            equ     1
DINCL_BITMAP            equ     1
DINCL_ROPS              equ     1

        include driver.inc
        include display.inc
        include egafam.inc
        include polyline.inc
        include extern.inc
        include protos.inc
if SCAN_CNT EQ 768
        include oem_macs.inc
        include assert.mac
PF_DEST_IS_DEV  EQU             8
EXTERNDEF       SCREEN_LPB      :ABS
endif

        .list

        .MODEL FLAT

        ASSUME  CS:FLAT,SS:FLAT,DS:FLAT,ES:FLAT

        .DATA

        PUBLIC  style_table
        PUBLIC  get_bm_info

        EXTERN  dev_solid_horizontal    :NEAR   ; PLYSOLID.ASM
        EXTERN  dev_solid_vertical      :NEAR   ; PLYSOLID.ASM
        EXTERN  dev_solid_diagonal      :NEAR   ; PLYSOLID.ASM
        EXTERN  dev_styled_horizontal   :NEAR   ; POLYSTYL.ASM
        EXTERN  dev_styled_vertical     :NEAR   ; POLYSTYL.ASM
        EXTERN  dev_styled_diagonal     :NEAR   ; POLYSTYL.ASM
        EXTERN  bitmap_solid_horizontal :NEAR   ; POLYBITM.ASM
        EXTERN  bitmap_solid_vertical   :NEAR   ; POLYBITM.ASM
        EXTERN  bitmap_solid_diagonal   :NEAR   ; POLYBITM.ASM
        EXTERN  bitmap_styled_horizontal:NEAR   ; POLYBITM.ASM
        EXTERN  bitmap_styled_vertical  :NEAR   ; POLYBITM.ASM
        EXTERN  bitmap_styled_diagonal  :NEAR   ; POLYBITM.ASM

;/*
;** the various line rendering routines
;*/

bitmap_styled_table     equ     this DWORD

        DWORD   bitmap_styled_horizontal
        DWORD   bitmap_styled_vertical
        DWORD   bitmap_styled_diagonal
        DWORD   bitmap_styled_diagonal

bitmap_solid_table      equ     this DWORD

        DWORD   bitmap_solid_horizontal
        DWORD   bitmap_solid_vertical
        DWORD   bitmap_solid_diagonal
        DWORD   bitmap_solid_diagonal

dev_solid_table         equ     this DWORD

        DWORD   dev_solid_horizontal
        DWORD   dev_solid_vertical
        DWORD   dev_solid_diagonal
        DWORD   dev_solid_diagonal

dev_styled_table        equ     this DWORD

        DWORD   dev_styled_horizontal
        DWORD   dev_styled_vertical
        DWORD   dev_styled_diagonal
        DWORD   dev_styled_diagonal

;/*
;** The bm_and_xor_table contains and-masks and xor-masks for
;** setting pels to one of four possible values.  The and-masks
;** have been inverted, and are stored in the low byte of each word.
;** The xor-masks are stored in the high bytes.
;**
;**       ROP     XOR    ~AND
;**       -------------------
;**       DDx     00      FF              set to zero
;**       Dn      FF      00              not dest
;**       D       00      00              leave alone
;**       DDxn    FF      FF              set to one
;*/

bm_and_xor_table        equ     this DWORD

        DWORD   000FFh                  ; set to zero
        DWORD   0FF00h                  ; not dest
        DWORD   00000h                  ; leave alone
        DWORD   0FFFFh                  ; set to one
;/*
;** The style_table contains style masks and their reverses, used for the
;** different line styles while drawing styled lines.
;*/

style_table     equ     this WORD

        db      10101010b, 01010101b    ; dotted line
        db      11001100b, 00110011b    ; short dash
        db      11100100b, 00100111b    ; dash dot
        db      10100000b, 00000101b    ; double dot
        db      11100111b, 11100111b    ; long dash
        db      11101010b, 01010111b    ; dash dot dot
        db      11111111b, 11111111b    ; solid
        db      00000000b, 00000000b    ; invisible
        db      10101010b, 01010101b    ; alternate dotted line

        .CODE

;/***************************************************************************
;*
;* FUNCTION NAME = do_polylines   
;*
;* DESCRIPTION   = Called by PolyLine.  Loops through all the line segments  
;*                 on the stack, computing the clipping required by          
;*                 polyline_workhorse_routine.  It then calculates all the   
;*                 DDA parameters and loops through the DDA.                 
;*                                                                           
;*                 Registers Destroyed:                                                                 
;*                       BX,CX,DX,SI,DI,DS                                                              
;*                 Registers Preserved:                                                                 
;*                       ES                                                                             
;*
;* INPUT         = DS:DI = DDC
;*                 
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = new style position
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/


;/*
;** NOTE: the save of di & si is REQUIRED for parameter alignment
;*/

ALIGN 4
ifndef IBMJ        ;IBMJ
do_polylines    PROC SYSCALL USES esi edi,      fsPolyFlags:DWORD,
                                                cLines:DWORD,
                                                npaptl:DWORD,
                                                usFunN:WORD,
                                                fComFlags:WORD
else  ;IBMJ        ;IBMJ
do_polylines    PROC SYSCALL USES esi edi,      pBound:DWORD,
                                                fsPolyFlags:DWORD,
                                                cLines:DWORD,
                                                npaptl:DWORD,
                                                usFunN:WORD,
                                                fComFlags:WORD
endif ;IBMJ        ;IBMJ
                        LOCAL   returnvalue:DWORD,
                                dwStyleCounter:DWORD,
                                Flags:DWORD,
                                Count:DWORD,
                                Points:DWORD
                        LOCAL   npddc:DWORD,
                                dwStyleMask:DWORD,
                                npfnUnSetup:DWORD,
                                CurLineColor:DWORD,
                                DrawModeIndex:DWORD,
                                cPlanes:DWORD
                        LOCAL   cbNextPlane:DWORD,
                                dwBitmapROP:DWORD,
                                dwStyleStepHorz:DWORD,
                                dwStyleStepVert:DWORD,
                                dwStyleStepDiag:DWORD
                        LOCAL   dwStyleStepHorzTmp:DWORD,
                                dwStyleStepVertTmp:DWORD,
                                offNextLine:DWORD,
                                cbScanSize:DWORD,
                                cbHugeScanWrap:DWORD,
                                cySurface:DWORD
                        LOCAL   cScansPerSegment:DWORD,
                                pSurfaceStart:DWORD,
                                anpfnRunRoutines:DWORD,
                                LineStyle:DWORD,
                                npfnPassSetup:DWORD,
                                cPasses:DWORD,
                                lsg:LINESEGMENT
                        LOCAL   dwStyleStepRun:DWORD,
                                dwStyleStepSide:DWORD,
                                dwActiveStyleCounter:DWORD,
                                dwActiveStyleMask:DWORD,
                                yA:DWORD
                        LOCAL   cScans:DWORD,
                                cA:DWORD,
                                cB:DWORD,
                                cm:DWORD
                        LOCAL   npfnRunRoutine:DWORD,
                                iWhichDDA:DWORD,
                                dwError:DWORD,
                                dwFracS:DWORD,
                                dwRotBitMask:DWORD
                        LOCAL   cbScanAddressDelta:DWORD,
                                cbSideStepAddressDelta:DWORD,
                                cbHugeScanDelta:DWORD,
                                selHugeDelta:DWORD,
                                dwScan:DWORD
                        LOCAL   pSurfaceAddress:DWORD,
                                cTmpScans:DWORD,
                                dwTmpError:DWORD,
                                dwTmpScan:DWORD,
                                dwTmpRotBitMask:DWORD
                        LOCAL   dwTmpActiveStyleCounter:DWORD,
                                dwTmpActiveStyleMask:DWORD,
                                cTmpPasses:DWORD,
                                TmpCurLineColor:DWORD
if SCAN_CNT EQ 768
                        LOCAL   start_bank:DWORD
endif


        ddc?    edi

        cld

if SCAN_CNT EQ 768
        mov     Flags,0
endif
        call    polyline_init

setup_polyline:

        mov     eax,fsPolyFlags
if SCAN_CNT EQ 768
        or      Flags,eax
else
        mov     Flags,eax
endif
        mov     eax,cLines
        mov     Count,eax
        mov     eax,npaptl
        mov     Points,eax
        mov     npddc,edi

;/*
;** copy information from the DDC to the stack                       
;*/


        cmp     usFunN,NGrePolyLine
        jnz     setup_disjointlines
 
;/*
;** draw first pel?
;*/

        ASSUME  edi:PTR DDC

        mov     offNextLine,SIZE POINTL
        test    [edi].ddc_fb,DDC_FIRST_PEL
        jz      setup_done
        or      Flags,PF_DRAW_FIRST_PEL
        jmp     setup_done
ALIGN 4

setup_disjointlines:

        add     Points,SIZE POINTL              ; Ignore current position
        mov     offNextLine,2 * SIZE POINTL
        shr     Count,1                 ; Count = cPoints / 2

setup_done:

        and     [edi].ddc_fb,not DDC_FIRST_PEL

;/*
;** the clip rectangle
;*/

        test    Flags,PF_BETTER_CLIP
        jz      no_clip_rect
        mov     ebx,[edi].ddc_prddc                ; ES:BX = RDDC

        rddc?   ebx

        ASSUME  ebx:PTR RDDC

        mov     eax,[ebx].rddc_arcsClip.rcl_xLeft
        mov     lsg.lsg_rcsClip.rcl_xLeft,eax
        mov     eax,[ebx].rddc_arcsClip.rcl_yBottom
        mov     lsg.lsg_rcsClip.rcl_yBottom,eax
        mov     eax,[ebx].rddc_arcsClip.rcl_xRight
        mov     lsg.lsg_rcsClip.rcl_xRight,eax
        mov     eax,[ebx].rddc_arcsClip.rcl_yTop
        mov     lsg.lsg_rcsClip.rcl_yTop,eax

no_clip_rect:

;/*
;** Loop through the lines                                           
;*/

walk_thru_polylines:

        mov     lsg.lsg_fsLineFlags,0

;/*
;** load the endpoints into registers
;*/

        mov     esi,Points
        mov     ebx,offNextLine         ; offset (bytes) between start of two lines
        add     Points,ebx

        ASSUME  esi:PTR POINTL

        mov     ebx,[esi].ptl_x                 ; (BX,CX) = point 1
        mov     ecx,[esi].ptl_y
        mov     edi,[esi][SIZE POINTL].ptl_x    ; (DI,SI) = point 2
        mov     esi,[esi][SIZE POINTL].ptl_y

;/*
;** handle non-clipped lines                                         
;*/

        test    Flags,PF_BETTER_CLIP
        jnz     clipped_line

;/*
;** make sure we go left to right
;*/

        cmp     ebx,edi
        jle     is_left_to_right
        xchg    ebx,edi
        xchg    ecx,esi
        or      lsg.lsg_fsLineFlags,LF_LEFT_XCHG

is_left_to_right:

;/*
;** record the start position (so we can calculate the surface address)
;*/

        mov     lsg.lsg_xStart,ebx
        mov     lsg.lsg_yStart,ecx

;/*
;** normalize coordinates relative to xStart and yStart
;*/

        sub     edi,ebx
        sub     esi,ecx
        jge     y_be_unsigned
        neg     esi
        or      lsg.lsg_fsLineFlags,LF_Y_FLIP + LF_VERTICAL_FLIP

y_be_unsigned:

;/*
;** get x-major
;*/

        cmp     esi,edi
        jbe     we_be_x_major
        xchg    esi,edi
        or      lsg.lsg_fsLineFlags,LF_XY_XCHG

we_be_x_major:

;/*
;** save the clip range
;*/

        mov     lsg.lsg_xA,0
        mov     lsg.lsg_xB,edi
        jmp     region_clipping_done
ALIGN 4

;/*
;** handle clipped lines                                             
;*/

;/*
;** load y clipping, normalize y coordinates relative to yStart
;*/

clipped_line:

        push    ebp          
        lea     ebp,lsg                 ; point to LINESEGMENT structure
        mov     edx,-1                  ; no POINTFX clipping!
        call    far_clip_line
        pop     ebp

region_clipping_done:

;/*
;** clip off the first pel, if needed                                
;*/

        cmp     usFunN,NGrePolyLine
        jnz     draw_it_only_once       ; DisjointLines always draws first pel
        test    Flags,PF_DRAW_FIRST_PEL
        jnz     draw_it_only_once
        test    lsg.lsg_fsLineFlags,LF_LEFT_XCHG
        jnz     is_left_exchanged
        cmp     lsg.lsg_xA,1
        jae     all_clipping_done
        mov     lsg.lsg_xA,1
        jmp     all_clipping_done
ALIGN 4

is_left_exchanged:

        cmp     lsg.lsg_xB,edi
        jb      all_clipping_done
        dec     edi
        mov     lsg.lsg_xB,edi
        inc     edi
        jnz     all_clipping_done       ; watch out for the zero length line!
;/*
;** Stick in the totally clipped case.  We have to let this fall through
;** the style advancing code!
;*/

draw_no_pels:

        mov     lsg.lsg_xB,0
        mov     lsg.lsg_xA,1
        jmp     all_clipping_done
ALIGN 4

;/*
;** if we draw the first pel this time, don't let it happen next time
;*/

draw_it_only_once:

        and     Flags,not PF_DRAW_FIRST_PEL

all_clipping_done:

;/*
;** compute the line style at both ends                              
;*/

        cmp     BYTE PTR LineStyle,LINETYPE_SOLID - 1
        jz      ignore_styles

        cmp     BYTE PTR LineStyle,LINETYPE_ALTERNATE - 1
        jnz     determine_major_styling

        mov     eax,dwStyleStepHorz
        mov     ecx,eax                 ;dwStyleStepVert = dwStyleStepHorz
        mov     dwStyleStepDiag,eax     ;dwStyleStepDiag =      "
        jmp     store_style_steps
ALIGN 4

;/*
;** Determine whether line is styled as x or y major.
;** If LF_XY_XCHG is true, then DI will be delta_y, but
;** we want it to be delta_x, so swap SI and DI.
;*/

determine_major_styling:

        mov     eax,dwStyleStepHorz
        mov     dwStyleStepHorzTmp,eax
        mov     ebx,dwStyleStepVert
        mov     dwStyleStepVertTmp,ebx

        test    lsg.lsg_fsLineFlags,LF_XY_XCHG
        jz      @F
        xchg    esi,edi                 ; SI = delta_y, DI = delta_x
@@:
        mul     edi                     ; DX:AX = x step * delta_x
        xchg    eax,ebx                 ; 
        mov     ecx,edx                 ; save all 32 bits
        mul     esi                     ; DX:AX = y step * delta_y
        cmp     edx,ecx
        jb      styled_as_x_major
        ja      styled_as_y_major
        cmp     eax,ebx
        ja      styled_as_y_major

styled_as_x_major:

        mov     dwStyleStepVertTmp,0
        jmp     set_diag_style_step
ALIGN 4

styled_as_y_major:

        mov     dwStyleStepHorzTmp,0

set_diag_style_step:

        mov     eax,dwStyleStepHorzTmp
        mov     ebx,dwStyleStepVertTmp
        mov     ecx,eax
        add     ecx,ebx
;/*
;** Determine major and side style steps.
;** If LF_XY_XCHG is true, then SI and DI will have been
;** swapped, so swap them back.
;*/

        test    lsg.lsg_fsLineFlags,LF_XY_XCHG
        jz      store_style_steps
        xchg    eax,ebx                 ; AX = major step
        xchg    esi,edi                 ; DI = major, SI = minor

store_style_steps:

        mov     dwStyleStepRun,eax
        mov     dwStyleStepDiag,eax
        mov     dwStyleStepSide,ecx

;/*
;** compute the change in style
;*/

        mov     ebx,edi                 ; change = RunStep * (major - minor)
        sub     ebx,esi                 ;        + SideStep * minor
        mul     ebx
        xchg    eax,ecx
        mul     esi
        add     eax,ecx                 ; AX = style change
        mov     ebx,dwStyleCounter      ; BX = style at start
        add     eax,ebx                 ; AX = style at finish
        cmp     usFunN,NGrePolyLine
        jnz     style_updated           ; Reset style for DisjointLines
        mov     dwStyleCounter,eax      ; style at start of next line

style_updated:

;/*
;** reverse the style if we've exchanged left for right
;*/

        mov     edx,dwStyleMask
        test    lsg.lsg_fsLineFlags,LF_LEFT_XCHG
        jz      @F
        mov     ebx,eax
        not     ebx                     ; BX = style count to use
        mov     dl,dh                   ; DL = style mask to use
@@:     mov     BYTE PTR dwActiveStyleCounter,bl
        mov     cl,bh
        and     cl,7
        rol     dl,cl
        mov     BYTE PTR dwActiveStyleMask,dl

;/*
;** do the styling version of a half-flip
;*/

        mov     eax,esi
        add     eax,eax
        cmp     eax,edi
        jbe     @F                      ; 2dy <= dx -- no half flip needed

flip_it_style:

        sub     esi,edi
        neg     esi
        xor     lsg.lsg_fsLineFlags,LF_HALF_FLIP + LF_VERTICAL_FLIP

;/*
;** exchange the style steps
;*/

        mov     eax,dwStyleStepRun
        xchg    eax,dwStyleStepSide
        mov     dwStyleStepRun,eax
        mov     dwStyleStepDiag,eax
@@:
        jmp     half_flips_done
ALIGN 4

;/*
;** do half flip for the non-styled case                             
;*/

ignore_styles:

        mov     eax,esi
        add     eax,eax
        cmp     eax,edi
        jbe     half_flips_done         ; 2dy <= dx -- no half flip needed

flip_it:

        sub     esi,edi
        neg     esi
        xor     lsg.lsg_fsLineFlags,LF_HALF_FLIP + LF_VERTICAL_FLIP

half_flips_done:

        mov     lsg.lsg_cdx,edi
        mov     lsg.lsg_cdy,esi

;/*
;** call the workhorse to compute the rendering                      
;*/

        mov     eax,lsg.lsg_xA                  ; get rid of clipped lines
        cmp     eax,lsg.lsg_xB
        ja      skip_this_line


        push    edi                             ;          
        mov     edi,npddc                       ;             
        mov     ax,fComFlags                    ;             
        and     al,DDC_VISIBLE                  ;             
        and     al,[edi].ddc_fb                 ;             
        pop     edi                             ;             
        je      skip_this_line                  ;             


        call    polyline_workhorse_routine

skip_this_line:

        dec     Count
        jnz     walk_thru_polylines

;/*
;** restore any hardware defaults
;*/

        call    npfnUnSetup

;/*
;** If the line is not solid and the background mix mode is not
;** transparent, then go back and draw the background line, unless we
;** have already.
;*/

        test    fsPolyFlags,PF_DOING_BACKGROUND
        jnz     done_background
        cmp     BYTE PTR LineStyle,LINETYPE_SOLID - 1
        jz      done_background
        mov     edi,npddc

        ASSUME  edi:PTR DDC

        cmp     [edi].ddc_la.la_ba.ba_bkmix,ROP_D    ; Leavealone
        je      done_background
        call    polyline_background_init
        or      fsPolyFlags,PF_DOING_BACKGROUND
        jmp     setup_polyline
ALIGN 4

done_background:

;/*
;** return the new style position
;*/

        mov     eax,dwStyleCounter

        RET

        OPTION  OLDMACROS
        OPTION  PROLOGUE:None
        OPTION  EPILOGUE:None

;/*
;** Define a DDA running macro.  We use eight specialized loops for     
;** speed.  Trying to make it just one leads to too many tests and jumps
;*/


;/*
;** define the various keywords
;*/

m?dda_huge macro
?dda_huge = 1
endm

m?dda_style macro
?dda_style = 1
endm

m?dda_rotate macro
?dda_rotate = 1
endm

m?dda_short macro
?dda_short = 1
endm

;/*
;** define the macro
;*/

run_the_dda     macro   mylist
        local   top_of_run
        local   skip_sel_update
        local   dest_bm
top_of_run:

;/*
;** determine what arguments we are given
;*/

?dda_huge = 0
?dda_style = 0
?dda_short = 0
?dda_rotate = 0
        irp     x,<mylist>
ifdef m?dda_&&x
        m?dda_&&x
else
        ??error2  <run_the_dda - unknown keyword x>
endif
        endm
;/*
;** handle the huge sidestep
;** WARNING: only set HUGE if cbSideStepAddressDelta is non-zero!
;*/

if SCAN_CNT NE 768
if ?dda_huge
        dec     dwScan
        jnz     @F
        mov     eax,cScansPerSegment    ;; reset dwScan
        mov     dwScan,eax
        add     edi,cbHugeScanDelta     ;; wrap the surface address
@@:
endif ; ?dda_huge
endif ;!768


;/*
;** handle normal sidestep and rotate
;*/

if ?dda_rotate
        ror     bl,1
        adc     edi,cbSideStepAddressDelta
else
        add     edi,cbSideStepAddressDelta
endif ; ?dda_rotate
if SCAN_CNT EQ 768
if ?dda_huge
        dec     dwScan
        jnz     skip_sel_update
        mov     eax,cScansPerSegment    ;; reset dwScan
        mov     dwScan,eax
        test    Flags, PF_DEST_IS_DEV
        jz      skip_sel_update


        push    edx
        mov     edx,start_bank
        add     edx,selHugeDelta
        SetBankRW
        pop     edx

        assert  edi,NE,pVRAMInstance
        mov     ecx,010000h
        cmp     edi,pVRAMInstance
        jb      @F
        neg     ecx
@@:
        add     edi,ecx
skip_sel_update:
endif ; ?dda_huge
endif ;768

;/*
;** handle styling
;*/

if ?dda_style
        mov     ecx,0
        mov     cl,BYTE PTR dwActiveStyleCounter
        add     ecx,dwStyleStepSide
        mov     BYTE PTR dwActiveStyleCounter,cl
        mov     cl,ch
        and     cl,7
        rol     BYTE PTR dwActiveStyleMask,cl
endif ; ?dda_style

;/*
;** go for the next scan
;*/

        dec     cScans
if ?dda_short
        jz      do_the_last_scan
else
        jz      do_the_last_scan
endif

;/*
;** determine the count for intermediate scans
;*/

        mov     ecx,cm
        mov     esi,dwError
        or      esi,esi
        js      @F
        inc     ecx
        sub     esi,lsg.lsg_cdy         ;; dwError -= 1
@@:     add     esi,dwFracS             ;; dwError += frac(s)
        mov     dwError,esi

;/*
;** loop back to the top, while calling the RunRoutine
;*/

        push    OFFSET top_of_run
        jmp     npfnRunRoutine          ; CX = number of pixels to draw
ALIGN 4
        endm

;/*
;** The table of DDA routines.                                       
;*/

WDDA_ROTATE     equ     0004h
WDDA_STYLE      equ     0008h
WDDA_HUGE       equ     00010h

        .errnz  WDDA_ROTATE - 4
        .errnz  WDDA_STYLE  - 8
        .errnz  WDDA_HUGE   - 16

        .DATA

dda_table       equ     this DWORD

        DWORD   dda____
        DWORD   dda___R
        DWORD   dda__S_
        DWORD   dda__SR
        DWORD   dda_H__
        DWORD   dda_H_R
        DWORD   dda_HS_
        DWORD   dda_HSR

        .CODE

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_workhorse_routine 
;*
;* DESCRIPTION   = the DDA algorithm used here is based on Jack Bresenham's Run 
;*                 Length Algorithm for Incremental Lines.  NATO ASI Series.    
;*                 Volume F17.  Fundamental Algorithms for Computer Graphics.   
;*                 Springer-Verlag Berlin Heidelberg 1985.                      
;*                                                                              
;*                 WARNING: this routine is called by DrawLines in DRAWLINE.ASM,                                                              
;*                       as well as by do_polyline.                                                                                           
;*                                                                              
;*                 Registers Destroyed:                                                                       
;*                       AX,BX,CX,DX,DI,SI                                                                    
;*                 Registers Preserved:                                                                       
;*                       DS,ES                                                                                
;*                                                                              
;* INPUT         = DI = cdx           
;*                 SI = cdy           
;*                 BP = polyline frame
;*                 
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

                PUBLIC  polyline_workhorse_routine

polyline_workhorse_routine::

        mov     iWhichDDA,0

;/*
;** check for horizontal lines
;*/

        or      esi,esi
        jnz     non_horizontal
        mov     yA,esi
        mov     cScans,esi
        mov     eax,lsg.lsg_xB
        sub     eax,lsg.lsg_xA
        inc     eax
        mov     cB,eax
        jmp     know_my_dda
ALIGN 4

non_horizontal:

;/*
;** On a 16 MHz 386, all the following clip calculations take only
;** 15 microseconds.  This seems to indicate that we don't need a
;** special case for simpler clipping!
;*/

;/*
;** Compute the various slope quantities:
;**
;**    m = floor(s)                                               (12)
;**
;**    dwFracS = frac(s)       (in units of 1/dy)
;*/

        mov     eax,edi
        xor     edx,edx
        div     esi
        mov     cm,eax
        mov     dwFracS,edx

;/*
;** Compute starting scan, remember that (2 dx) might be 17 bits!        
;**                                                                      
;**    y  = ceil((x - f )/s + 1/2) - 1                                   
;**     a          a   0                                                 
;**                                                                      
;**       = floor((x - f )/s + 1/2 - 1/(2 dx))                    (27)   
;**                 a   0                                                
;**                                                                      
;**       = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))                 
;**                      a         0                                     
;*/

        add     esi,esi                 ; SI = (2 dy)
        mov     eax,lsg.lsg_xA          ; 2 dy xA
        mul     esi                     ; 
        mov     ebx,edi                 ; dx - 1
        dec     ebx
        xor     ecx,ecx
        cmp     byte ptr lsg.lsg_fsLineFlags[1],1 ; -2 dy f   (i.e. +1 if flip)

        .errnz  LF_VERTICAL_FLIP - 100h           ;        0

        cmc
        adc     eax,ebx
        shr     eax,1                   ; / 2 
        adc     ecx,ecx
        div     edi                     ; / dx
        mov     yA,eax                  ; remainder rm = 2 * DX + CX

;/*
;** Compute the first run:
;**
;**    c  = floor(s(y + 1/2) + f ) - x + 1
;**     a            a          0     a
;**                                                               (30)
;**                    rm + 1
;**       = floor( s - ------ ) + 1
;**                     2 dy
;*/

        mov     eax,edi
        sub     eax,edx
        xor     edx,edx
        add     eax,eax
        inc     ecx
        sub     eax,ecx
        div     esi
        inc     eax
        mov     cA,eax

;/*
;** Compute the initial error term:
;**
;**    f' = frac(s(y + 1/2) + f ) + frac(s) - 1
;**     1           a          0
;**                                                               (31)
;**                   rm + 1
;**       = frac( s - ------ ) + frac(s) - 1.
;**                    2 dy
;**
;**
;** DX is presently frac(s - (rm + 1)/(2 dy)) in units of 1/(2 dy).
;** If we were to express frac(s) and 1 in units of 1/(2 dy), they would
;** each be EVEN numbers.  Therefore the bottom bit of f' will be unchanged
;** whenever we calculate f' <- frac(f') + frac(s) - 1.  This bottom bit
;** is therefore wasted.  Since (f' >= 0) is unchanged if we turn off the
;** bottom bit, this is what we will do.  On the other hand, f' needs to
;** be a signed number and we therefore need the top bit to be significant.
;** The result of all this is that we will shift the bottom bit off of
;** the unsigned DX, and keep f' (dwError) in units of 1/dy.
;*/

        shr     edx,1
        add     edx,dwFracS
        sub     edx,lsg.lsg_cdy
        mov     dwError,edx             ; that was easy!

;/*
;** Compute the final scan, remember that (2 dx) might be 17 bits!
;**
;**    y  = ceil((x - f )/s + 1/2) - 1                            (23)
;**     b          b   0
;**
;**       = floor((x - f )/s + 1/2 - 1/(2 dx))                    (27)
;**                 b   0
;**
;**       = floor((2 dy x  - 2 dy f  + dx - 1) / (2 dx))
;**                      b         0
;*/

        mov     eax,lsg.lsg_xB          ; 2 dy xB
        mul     esi
        mov     ebx,edi                 ; dx - 1
        dec     ebx
        xor     ecx,ecx
        cmp     byte ptr lsg.lsg_fsLineFlags[1],1 ; -2 dy f

        .errnz  LF_VERTICAL_FLIP - 100h           ;        0

        cmc
        adc     eax,ebx
        shr     eax,1                   ; / 2
        adc     ecx,ecx
        div     edi                     ; / dx, remainder rm' = 2 * DX + CX

;/*
;** if both scans are the same, we're clipped to be horizontal
;*/

        sub     eax,yA
        mov     cScans,eax
        jnz     still_non_horizontal
        mov     eax,lsg.lsg_xB
        sub     eax,lsg.lsg_xA
        inc     eax
        mov     cB,eax
        jmp     know_my_dda
ALIGN 4

still_non_horizontal:

;/*
;** Compute the final run:
;**
;**    c  = x  - floor(s(y - 1/2) + f )
;**     b    b            b          0
;**                                                               (32)
;**                   rm'+ 1           rm'+ 1
;**       = - floor(- ------ ) = ceil( ------ )
;**                    2 dy             2 dy
;*/

        mov     eax,edx
        xor     edx,edx
        add     eax,eax
;DAK    adc     edx,edx
        inc     ecx
        add     eax,ecx
;DAK    adc     edx,0
        div     esi
        neg     edx                     ; round any remainder up
        adc     eax,0
        mov     cB,eax

know_my_dda:

;/*
;** Advance the style mask for clipping.                             
;*/

        cmp     BYTE PTR LineStyle,LINETYPE_SOLID-1
        jz      no_style_adjust
        or      iWhichDDA,WDDA_STYLE
        mov     eax,lsg.lsg_xA
        mov     ebx,yA
        sub     eax,ebx
        mul     dwStyleStepRun
        xchg    eax,ebx
        mul     dwStyleStepSide
        add     eax,ebx
        add     BYTE PTR dwActiveStyleCounter,al
        adc     ah,0
        mov     cl,ah
        and     cl,7
        rol     BYTE PTR dwActiveStyleMask,cl

no_style_adjust:

;/*
;** Rotate the clipped start point and sidestep back to real            
;** coordinates so we can calculate surface addresses.                  
;*/

;/*
;** load the first pixel
;*/

        mov     ebx,lsg.lsg_xA                  ; (BX,AX) = first pixel
        mov     eax,yA

;/*
;** Load a description of the side step.  This defaults to a (1,1) diagonal
;** step.  A step up requires subtraction of the scan size.  A step to the
;** right requires a bit rotation.  We hold this information as two flags:
;** CL = the bit rotation, CH = (BOOL) (do we move a scan?).  We supplement
;** this information with DX = CH * scansize.  We'll also carry along the
;** scan size since it must get negated if we are Y flipped.
;*/

        mov     esi,cbScanSize
        neg     esi                     ; default up => negative address delta
        mov     ecx,101h                ; CH = move by scan, CL = rotate bit

;/*
;** HALF FLIP
;*/

        mov     edi,lsg.lsg_fsLineFlags ; DI = flags
        test    edi,LF_HALF_FLIP
        jz      @F
        sub     eax,ebx                 ; y = x - y
        neg     eax
        movzx   ecx,cl
@@:

;/*
;** XY XCHG
;*/

        test    edi,LF_XY_XCHG
        jz      @F
        xchg    eax,ebx
        xchg    ch,cl
@@:

;/*
;** Y FLIP
;*/

        test    edi,LF_Y_FLIP
        jz      @F
        neg     eax                     ; y = -y
        neg     esi
@@:

        shl     cl,2

        .errnz  WDDA_ROTATE - 4

        or      byte ptr iWhichDDA,cl
        mov     cbScanAddressDelta,esi
        neg     ch
        movsx   ecx,cx
        sbb     ecx,ecx
        and     esi,ecx
        mov     cbSideStepAddressDelta,esi ; SI = SideStep delta

;/*
;** Compute the surface address.                                     
;*/

        add     ebx,lsg.lsg_xStart              ; BX = pixel in scan
        add     eax,lsg.lsg_yStart

ifdef FIREWALLS
 or eax,eax
 jns @F
        int 3
endif
@@:
        mov     edx,cySurface           ; surface top is at low addresses
        dec     edx                     ;  => invert y coordinate
        sub     edx,eax                 ; DX = scan line

;/*
;** handle adjustment of huge surfaces
;*/

        mov     ecx,cScansPerSegment

if SCAN_CNT EQ 768

;/*
;** Start Multiple Bank Stuff
;**
;**       esi =  cbSideStepAddressDelta
;**       ecx =  NOT Device: cScansPerSegment = 0
;**                  Device: SCREEN_LPB
;**       edx =  scan line
;*/

        test    Flags,PF_DEST_IS_DEV
        jz      not_huge_surface

        mov     ecx,SCREEN_LPB

        push    eax                     ; Initialize to bank 0
        push    edx
        mov     edx,0
        mov     start_bank,edx
        SetBankRW
        pop     edx
        pop     eax

        or      esi,esi                 ; use: SI = SideStep delta
        jz      @F
        or      iWhichDDA,WDDA_HUGE
@@:

;/*
;** edx = scan line
;** ecx = SCREEN_LPB
;*/

        cmp     edx,ecx
        jb      no_huge_adjust_needed
        mov     eax,edx
        xor     edx,edx
        div     ecx                     ; DX = scan in final segment

        push    eax
        push    edx
        mov     edx,eax
        mov     start_bank,edx
        SetBankRW
        pop     edx
        pop     eax
no_huge_adjust_needed:

;/*
;** decide what the huge adjustments are
;*/

        sub     ecx,edx                 ; CX = number of remaining scans
        mov     eax,cbHugeScanWrap      ;        in this segment

        mov     esi,1
        test    edi,LF_Y_FLIP
        jnz     @F
        mov     ecx,edx
        inc     ecx
        neg     eax
        neg     esi
@@:
        mov     selHugeDelta,esi
        mov     cbHugeScanDelta,eax

;/*
;** End Multiple Bank Stuff
;*/

endif
not_huge_surface:

        mov     dwScan,ecx              ; number of remaining scans

;/*
;** compute the offset and bit mask
;*/

        mov     eax,cbScanSize
        mul     edx
        shl     ebx,16                  ; discard the high word, use only the
        rol     ebx,16                  ; low word for calculating the offset
        mov     ecx,ebx
        shr     ebx,3
        add     eax,ebx
        add     eax,pSurfaceStart
        mov     pSurfaceAddress,eax
        and     cl,7
        mov     al,80h
        ror     al,cl
        mov     BYTE PTR dwRotBitMask,al

;/*
;** Determine the Run Routine.                                       
;*/

        mov     esi,edi
        and     esi,110b
        shl     esi,1

        .errnz  LF_HALF_FLIP + LF_XY_XCHG - 110b

        add     esi,anpfnRunRoutines
        lodsd
        mov     npfnRunRoutine,eax

;/*
;** Setup for a pass.                                                
;*/

;/*
;** copy all the variables that the DDA loop will destroy
;*/

        mov     eax,cPasses
        mov     cTmpPasses,eax
        mov     al,BYTE PTR CurLineColor
        mov     BYTE PTR TmpCurLineColor,al

do_another_pass:

;/*
;** load the surface address (we assume the segment is loaded)
;*/

        mov     edi,pSurfaceAddress     ; DS:DI = surface address

;/*
;** call the per pass setup routine
;*/

        call    npfnPassSetup
        or      eax,eax
        jz      do_next_pass

;/*
;** Run the DDA!                                                     
;*/

        mov     bl,BYTE PTR dwRotBitMask                ; BL = rotating bit mask

;/*
;** send a single scan to the end
;*/

        cmp     cScans,0
        jz      do_the_last_scan

;/*
;** draw the first scan
;*/

        mov     ecx,cA
        mov     esi,iWhichDDA
        push    dda_table[esi]
        jmp     npfnRunRoutine          ; CX = number of pixels to draw
ALIGN 4

;/*
;** our favorite DDA routines
;*/

dda____:        run_the_dda     <short>

dda___R:        run_the_dda     <short,rotate>

;/*
;** Finish up the workhorse routine.                                 
;*/

;/*
;** dump out the last scan
;*/

do_the_last_scan:

        mov     ecx,cB
        call    npfnRunRoutine

;/*
;** see if we need another pass
;*/

do_next_pass:

        dec     cTmpPasses
        jz      workhorse_done
        jmp     do_another_pass
ALIGN 4

workhorse_done:

        ret

;/*
;** More DDA routines.                                               
;*/



dda__S_:        run_the_dda     <short     ,style       >
dda__SR:        run_the_dda     <short     ,style,rotate>
dda_H__:        run_the_dda     <      huge             >
dda_H_R:        run_the_dda     <      huge      ,rotate>
dda_HS_:        run_the_dda     <      huge,style       >
dda_HSR:        run_the_dda     <      huge,style,rotate>


        PUBLIC  device_unsetup

ALIGN 4
device_unsetup::

        mov     dx,EGA_BASE+GRAF_ADDR
        mov     ax,GRAF_ENAB_SR         ; disable all planes for set_reset.
        out     dx,ax
        mov     ax,DR_SET*256 + GRAF_DATA_ROT
        out     dx,ax                   ; set, no rotation.
        mov     ax,0FF00h+GRAF_BIT_MASK
        out     dx,ax                   ; enable all bits.
        mov     ax,MM_ALL               ; enable all planes.
        mov     dl,SEQ_DATA
        out     dx,al
        ret

        PUBLIC  device_one_pass_init

;/***************************************************************************
;*
;* PUBLIC ROUTINE  device_one_pass_init 
;*
;* DESCRIPTION   = Sets up the ega to handle one of the twelve raster        
;*                 operations which can be done in one pass of EGA memory.   
;*                                                                           
;*                 Registers Destroyed:                                                               
;*                       AX,BX,DX                                                                     
;*                 Registers Preserved:                                                               
;*                       CX,DI,SI,BP,DS,ES  
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                         ---Pseudo-Code--
;*  device_one_pass_init
;*  {
;*     // this routine sets up the EGA to handle one of the twelve
;*     // raster operations which can be done in one pass of EGA memory.
;* 
;*     // set up the set/reset register.
;* 
;*     TmpColor &= PenANDFlag;
;*     TmpColor ^= PenXORFlag;
;*     set set/reset register to TmpColor;
;* 
;*     enable all planes for set/reset;
;* 
;*     set data rotate register to DataROTFlag;
;* 
;*     // the bitmask register must be the last register written to the
;*     // graphics controller so that its address is still in the
;*     // address register.
;* 
;*     enable all bits in bitmask register;
;* 
;*     enable all planes for writing in the sequencer;
;* 
;*     return();
;*  }
;*
;**************************************************************************/

device_one_pass_init::

        mov     ebx,DrawModeIndex
        mov     ah,BYTE PTR CurLineColor
        and     ah,Code_rop_pen_and[ebx]
        xor     ah,Code_rop_pen_xor[ebx]
        mov     al,GRAF_SET_RESET       ; Write to set/reset register
        mov     dx,EGA_BASE + GRAF_ADDR
        out     dx,ax

        mov     ah,MM_ALL               ; enable all planes for set_reset
        mov     al,GRAF_ENAB_SR
        out     dx,ax

        mov     ah,Code_rop_data_r[ebx]  ; Set DataRotate register value
        mov     al,GRAF_DATA_ROT
        out     dx,ax
;/*
;** The bitmask register must be the last register written to the
;** Graphics Controller so that it's address is still in the
;** address register.
;*/

set_bit_mask:

        mov     eax,0FF00h+GRAF_BIT_MASK
        out     dx,ax

        mov     al,MM_ALL               ; enable all planes for writing
        mov     dl,SEQ_DATA
        out     dx,al
        ret

        PUBLIC  device_one_pass

ALIGN 4
device_one_pass::

        mov     dx,EGA_BASE + GRAF_DATA
        mov     eax,1
        ret

        PUBLIC  device_first_pass_of_two

;/***************************************************************************
;*
;* PUBLIC ROUTINE   device_first_pass_of_two
;*
;* DESCRIPTION   =  Sets up the EGA for the four raster operations which    
;*                  take two passes of EGA memory.  These raster operations 
;*                  are all handled in a similar fashion:  on the first pass
;*                  we set the necessary bits of the necessary planes to    
;*                  ones or zeros explicitly.  The second pass we invert the
;*                  destination bits for the remaining planes.              
;*
;*                  Registers Destroyed:     
;*                        BX                 
;*                  Registers Preserved:     
;*                        CX,DI,SI,BP,DS,ES  
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1                    
;*                 DX = EGA_BASE + GRAF_DATA 
;* RETURN-ERROR  = NONE
;*
;*                       ----Pseudo-Code-----
;* BOOL device_first_pass_of_two
;* {
;*    // this routine sets up the EGA for the four raster operations which
;*    // take two passes of EGA memory.  these raster operations are all
;*    // handled in a similar fashion: on the first pass we set the necessary
;*    // bits of the necessary planes to ones or zeros explicitly. the second
;*    // pass we invert the destination bits for the remaining planes.
;*
;*    set set/reset register to PenANDFlag;
;*
;*    set TmpColor to the current pen or background color;
;*    TmpColor ^= PenXORFlag;            // invert the color if necessary.
;*    TmpColor &= MM_ALL;
;*
;*    // enable only the planes that need it.
;*
;*    set enable set/reset register to TmpColor;
;*
;*    set data rotate register to DataROTFlag;
;*
;*    // the bitmask register must be the last register written to the
;*    // graphics controller so that its address is still in the
;*    // address register.
;*
;*    enable all bits in bit mask register;
;*
;*    reset TmpColor to current pen or background color;
;*    TmpColor ^= PenXORFlag;            // invert color if necessary.
;*
;*    // enable only necessary planes for writing.
;*
;*    set sequencer data register to TmpColor;
;*
;*    return();
;* }
;*
;***************************************************************************/

ALIGN 4
device_first_pass_of_two::

;/*
;** make copies of variables that a pass will destroy
;*/

        mov     eax,cScans
        mov     cTmpScans,eax
        mov     eax,dwError
        mov     dwTmpError,eax
        mov     al,BYTE PTR dwActiveStyleCounter
        mov     BYTE PTR dwTmpActiveStyleCounter,al
        mov     al,BYTE PTR dwActiveStyleMask
        mov     BYTE PTR dwTmpActiveStyleMask,al

;/*
;** setup the EGA
;*/

        mov     ebx,DrawModeIndex
        mov     ah,Code_rop_pen_and[ebx]
        mov     al,GRAF_SET_RESET       ;Write to set/reset register
        mov     dx,EGA_BASE + GRAF_ADDR
        out     dx,ax

        mov     ah,BYTE PTR CurLineColor        ; get the color.
        xor     ah,Code_rop_pen_xor[ebx]  ; invert color if necessary.
        and     ah,MM_ALL               ; enable only the planes that need it.
        mov     al,GRAF_ENAB_SR
        out     dx,ax

        mov     ah,Code_rop_data_r[ebx]  ;Set DataRotate register value
        mov     al,GRAF_DATA_ROT
        out     dx,ax
;/*
;** The bitmask register must be the last register written to the
;** Graphics Controller so that it's address is still in the
;** address register.
;*/

        mov     ax,0FF00h+GRAF_BIT_MASK
        out     dx,ax

        mov     al,BYTE PTR CurLineColor        ; get the color.
        xor     al,Code_rop_pen_xor[ebx]  ; invert color if necessary.
        and     al,MM_ALL               ; enable only necessary planes for writing.
        mov     dl,SEQ_DATA
        out     dx,al
        mov     dl,GRAF_DATA
        mov     npfnPassSetup,OFFSET device_second_pass_of_two
        mov     eax,1
        ret

        PUBLIC  device_second_pass_of_two

;/***************************************************************************
;*
;* PUBLIC ROUTINE   device_second_pass_of_two 
;*
;* DESCRIPTION   =  We have one of the four raster operations which cannot be 
;*                  done in one pass of EGA memory.  The first pass will have 
;*                  set the necessary bits in the necessary planes to either  
;*                  1's or 0's explicitly.  This pass will NOT the destination
;*                  for the necessary bits in the necessary planes.  The EGA  
;*                  has to be set up to not the destination, before the line  
;*                  is drawn.                                                 
;*
;*                  Registers Destroyed:    
;*                        BX                
;*                  Registers Preserved:    
;*                        CX,DI,SI,BP,DS,ES 
;*
;* INPUT         = AX = 1   if the line needs to be drawn                                                                    
;*                 AX = 0   if the line does not need to be drawn 
;*                 DX = EGA_BASE + GRAF_DATA                      
;*                 
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;*                      -------Pseudo-Code-----
;*   BOOL device_second_pass_of_two
;*   {
;*      TmpColor = current pen or background color;
;*      TmpColor ^= PenXORFlag;            // invert color if necessary.
;*      TmpColor = ! TmpColor;             // get the other planes.
;*  
;*      // enable the planes not enabled in first pass.
;*  
;*      set sequencer data register to TmpColor;
;*  
;*      set EGA to XOR mode;
;*  
;*      enable all bits in bit mask register;
;*   }
;**************************************************************************/

ALIGN 4
device_second_pass_of_two::

;/*
;** restore variables destroyed by the first pass
;*/

        mov     eax,cTmpScans
        mov     cScans,eax
        mov     eax,dwTmpError
        mov     dwError,eax
        mov     al,BYTE PTR dwTmpActiveStyleCounter
        mov     BYTE PTR dwActiveStyleCounter,al
        mov     al,BYTE PTR dwTmpActiveStyleMask
        mov     BYTE PTR dwActiveStyleMask,al

;/*
;** setup the EGA for pass 2
;*/

        mov     al,BYTE PTR CurLineColor        ; get the color
        mov     ebx,DrawModeIndex
        xor     al,Code_rop_pen_xor[ebx]  ; invert color if necessary
        not     al                      ; enable the other planes
        and     eax,MM_ALL
        jz      standard_ega_get_out    ; no bits to XOR

        mov     dx,EGA_BASE+SEQ_DATA
        out     dx,al

        mov     dl,GRAF_ADDR            ; set to XOR mode
        mov     ax,DR_XOR * 256 + GRAF_DATA_ROT
        out     dx,ax

        mov     ax,0FF00h+GRAF_BIT_MASK ; set up bitmask register
        out     dx,ax

        mov     dl,GRAF_DATA
        mov     eax,1

standard_ega_get_out:

        mov     npfnPassSetup,OFFSET device_first_pass_of_two
        ret

        PUBLIC  bitmap_pass_per_plane

;/***************************************************************************
;*
;* PUBLIC ROUTINE  bitmap_pass_per_plane
;*
;* DESCRIPTION   = This routine is called before each pass over a bitmap.  With
;*                 bitmaps we can break the operation down into four           
;*                 categories.  Set some bits to zero, set some bits to one,   
;*                 xor some bits, or do nothing.  Which operation to be used is
;*                 determined by the raster operation, and whether or not the  
;*                 current bits in the current color plane are set.            
;*                 dwBitmapROP breaks the operation into two pieces.  If the   
;*                 low byte is FF, we first turn OFF all the bits indicated.   
;*                 If the high byte is FF, we XOR the bits indicated.  This    
;*                 produces all four operations correctly.  Obviously, if      
;*                 dwBitmapROP is 0000, there is no need to draw the line in   
;*                 this pass.                                                  
;*                 
;*                 Registers Destroyed: 
;*                       BX             
;*                 Registers Preserved: 
;*                       CX,DI,SI,BP,ES 
;*                 
;* INPUT         = AX = 1   if the line needs to be drawn         
;*                 AX = 0   if the line does not need to be drawn 
;*                 DS = bitmap selector (set here if huge)        
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
bitmap_pass_per_plane::

;/*
;** save any variables we're going to destroy
;*/

        mov     eax,cPlanes
        cmp     eax,1
        jz      bitmap_vars_all_saved
        cmp     cTmpPasses,eax
        jnz     restore_variables
        mov     eax,cScans
        mov     cTmpScans,eax
        mov     eax,dwError
        mov     dwTmpError,eax
        mov     al,BYTE PTR dwActiveStyleCounter
        mov     BYTE PTR dwTmpActiveStyleCounter,al
        mov     al,BYTE PTR dwActiveStyleMask
        mov     BYTE PTR dwTmpActiveStyleMask,al
        mov     eax,dwScan
        mov     dwTmpScan,eax
        jmp     bitmap_vars_all_saved
ALIGN 4

;/*
;** on later passes, restore destroyed variables
;*/

restore_variables:

        mov     eax,cTmpScans
        mov     cScans,eax
        mov     eax,dwTmpError
        mov     dwError,eax
        mov     al,BYTE PTR dwTmpActiveStyleCounter
        mov     BYTE PTR dwActiveStyleCounter,al
        mov     al,BYTE PTR dwTmpActiveStyleMask
        mov     BYTE PTR dwActiveStyleMask,al
        mov     eax,dwTmpScan
        mov     dwScan,eax

bitmap_vars_all_saved:
;/*
;** Advance the start address to the next plane.  This doesn't have any
;** effect until the next pass.
;*/

        mov     eax,cbNextPlane
        add     pSurfaceAddress,eax

;/*
;** load the starting selector if this is a huge bitmap
;*/

;/*
;** determine the logical operation to do
;*/

        mov     ebx,DrawModeIndex       ; get the raster op
        ror     BYTE PTR TmpCurLineColor,1      ; see if the color plane is set
        jnc     set_the_standard_proc
        shr     ebx,2

set_the_standard_proc:

        and     ebx,3
        shl     ebx,2                   ; make a word ptr
        mov     eax,bm_and_xor_table[ebx] ; add index into table
        mov     dwBitmapROP,eax         ; set up write logic
        ret

;/***************************************************************************
;*
;* PUBLIC ROUTINE   dev_polyline_init 
;*
;* DESCRIPTION   =  Initializes stack frame variables for drawing lines to the 
;*                  display. 
;*                 
;*                  Registers Destroyed:   
;*                        AX,BX,CX,DX,SI,DI
;*                  Registers Preserved:   
;*                        BP               
;*
;* INPUT         = DS:DI => DDC    (ignored)   
;* OUTPUT        = DS = EGA selector  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
dev_polyline_init:

;/*
;** Depending on whether the raster operation coming in can be done in one pass
;** of EGA memory or not, we set up the EGA differently.
;*/

        mov     ebx,DrawModeIndex
        mov     bl,Code_rop_flags[ebx]
        mov     ecx,2
        mov     eax,OFFSET device_first_pass_of_two
        test    bl,SINGLE_OK
        jz      better_do_double
        call    device_one_pass_init
        mov     ecx,1
        mov     eax,OFFSET device_one_pass

better_do_double:

        mov     cPasses,ecx
        mov     npfnPassSetup,eax
        mov     npfnUnSetup,OFFSET device_unsetup

;/*
;** decide which set of run routines to use
;*/

        mov     eax,OFFSET dev_styled_table
        cmp     BYTE PTR LineStyle,LINETYPE_SOLID - 1
        jnz     @F
        mov     eax,OFFSET dev_solid_table
@@:     mov     anpfnRunRoutines,eax

;/*
;** record the surface parameters
;*/

        mov     cySurface,SCREEN_CY
        mov     cbScanSize,SCREEN_CBSCAN
        mov     cScansPerSegment,0
if SCAN_CNT EQ 768
        mov     cbHugeScanWrap,0
endif
        mov     eax,sdScreen.sd_pBits
        mov     pSurfaceStart,eax
        ret

;/***************************************************************************
;*
;* PUBLIC ROUTINE  bm_polyline_init  
;*
;* DESCRIPTION   = Initializes stack frame variables for drawing lines to a bitmap.
;*
;*                 Registers Destroyed:  
;*                       AX,BX,CX        
;*                 Registers Preserved:  
;*                       DX,DI,SI,BP,ES  
;*
;* INPUT         = DS:DI = DDC 
;* OUTPUT        = DS = first segment of bitmap  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
bm_polyline_init::

        call    get_bm_info             ; takes DS:DI = DDC

;/*
;** decide which set of run routines to use
;*/

        mov     eax,OFFSET bitmap_solid_table
        cmp     BYTE PTR LineStyle,LINETYPE_SOLID - 1
        jz      @F
        mov     eax,OFFSET bitmap_styled_table
@@:     mov     anpfnRunRoutines,eax

;/*
;** set the routines to run on each pass
;*/

        mov     eax,cPlanes
        mov     cPasses,eax
        mov     npfnPassSetup,OFFSET bitmap_pass_per_plane
        mov     npfnUnSetup,OFFSET NullRoutine
        ret

        PUBLIC  NullRoutine

NullRoutine::

        ret                             ; doesn't do much!

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_bm_info       
;*
;* DESCRIPTION   =  Loads bitmap information from the DDC into local variables.
;*
;*                 Registers Destroyed:  
;*                       AX,BX,CX        
;*                 Registers Preserved:  
;*                       DX,DI,SI,BP,ES  
;*
;* INPUT         = DS:DI => DDC  
;* OUTPUT        = DS = first segment of bitmap  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
get_bm_info::

        ddc?    edi

;/*
;** Color bitmaps are assumed to be four planes deep.
;*/

        mov     ebx,[edi].ddc_npsd      ; get ptr to surface definition
        mov     ecx,BITS_PEL            ; assume color

        .errnz  BITS_PEL - 4

        ASSUME  ebx:PTR SURFACE

        test    [ebx].sd_fb,SD_COLOR
        jnz     @F
        mov     cl,1                    ; nope, we're mono
        shr     BYTE PTR CurLineColor,4         ; move mono bit to lsb

        .errnz  MONO_BIT - 16           ; 16 = 2^4

@@:     mov     cPlanes,ecx             ; CX = number of planes

        mov     eax,[ebx].sd_cy
        mov     cySurface,eax           ; height of destination

;/*
;** Bitmaps are stored in groups of scan lines.  For example, with three color
;** planes, the bitmap is stored as a scan line of the first color plane, a scan
;** line of the second color plane, a scan line of the third color plane, a scan
;** line of the first color plane, and so on. Therefore, when writing to a color
;** plane, a move to the next scan line actually has to jump over three scan
;** lines.
;*/

        mov     eax,[ebx].sd_cbScan     ; the next plane starts with the
        mov     cbNextPlane,eax         ;   next scan line
        mul     ecx                     ; compute where the next scan begins
        mov     cbScanSize,eax
;/*
;** Check to see if we have a small or a huge bitmap.  If it is huge, then this
;** is a good time to load huge bitmap information.  If it is small, skip this
;** section.
;*/

        xor     eax,eax

get_bm_info_exit:

        mov     cScansPerSegment,eax    ; zero for non-huge bitmap
        mov     eax,[ebx].sd_pBits
        mov     pSurfaceStart,eax       ; offset of memory bitmap
        ret

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_init 
;*
;* DESCRIPTION   = Do common initialization for polylines and scanlines.     
;*
;*                  Registers Destroyed:        
;*                        AX,BX                 
;*                  Registers Preserved:        
;*                        CX,DX,SI,DI,BP,DS,ES  
;*
;* INPUT         = DS:DI = DDC
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

                PUBLIC  polyline_init

ALIGN 4
polyline_init::

        call    get_line_attributes

;/*
;** the surface information (and maybe hardware setup)
;*/

        mov     ebx,OFFSET bm_polyline_init
        test    [edi].ddc_fb,DDC_DEVICE
        jz      @F
        mov     ebx,OFFSET dev_polyline_init
if SCAN_CNT EQ 768
        or      Flags,PF_DEST_IS_DEV      ;gets moved into Flags at
endif
@@:     call    ebx
        ret

;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_line_attributes 
;*
;* DESCRIPTION   = Initialize local variables with line color and style,     
;*                 and raster operation.                                   
;*                                                                          
;*                 Registers Destroyed:        
;*                       AX,BX                 
;*                 Registers Preserved:        
;*                       CX,DX,SI,DI,BP,DS,ES  
;*                
;* INPUT         = DS:DI = DDC
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
get_line_attributes:

        ddc?    edi

;/*
;** Get line color
;*/

        mov     al,BYTE PTR [edi].ddc_la.la_ba.ba_ipc.ipc_bClr
        mov     BYTE PTR CurLineColor,al

;/*
;** get line style
;*/

        mov     ebx,[edi].ddc_la.la_usType
        mov     BYTE PTR LineStyle,bl           ;!!! how about a bit somewhere ???

        add     ebx,ebx
        movzx   eax,style_table[ebx]    ; a style mask from the table, and
        mov     dwStyleMask,eax         ; set the style flags.

        mov     ax,WORD PTR [edi].ddc_la.la_bStepX  ; AL=step x,AH=step y

        .errnz  LINE_ATTRS.la_bStepY - LINE_ATTRS.la_bStepX - 1

        xor     ebx,ebx
        xchg    bl,ah                   ; AX = X step, BX = Y step
        mov     dwStyleStepHorz,eax
        mov     dwStyleStepVert,ebx

;/*
;** handle LINETYPE_ALTERNATE specially
;*/

        cmp     BYTE PTR LineStyle,LINETYPE_ALTERNATE - 1
        jnz     @F
        mov     eax,100h
        mov     dwStyleStepHorz,eax
        mov     dwStyleStepVert,eax
@@:

        mov     ax,WORD PTR [edi].ddc_la.la_bError ; AL=error, AH=mask position

        .errnz  LINE_ATTRS.la_bMask - LINE_ATTRS.la_bError - 1

        mov     dwStyleCounter,eax

;/*
;** Get the raster operation to use
;*/

        mov     bl,[edi].ddc_la.la_ba.ba_bmix
        and     ebx,RASTER_OP_MASK      ;  (play it safe)
        mov     DrawModeIndex,ebx       ;  and save a copy
        ret

;/*
;** Now do
;*/

;/***************************************************************************
;*
;* PUBLIC ROUTINE  polyline_background_init  
;*
;* DESCRIPTION   = Do common initialization for polylines and scanlines. 
;*
;*                  Registers Destroyed:       
;*                        AX,BX                
;*                  Registers Preserved:       
;*                        CX,DX,SI,DI,BP,DS,ES 
;*
;* INPUT         = DS:DI = DDC  
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

                PUBLIC  polyline_background_init

ALIGN 4
polyline_background_init::

        call    get_background_line_attributes

;/*
;** the surface information (and maybe hardware setup)
;*/

        mov     ebx,OFFSET bm_polyline_init
        test    [edi].ddc_fb,DDC_DEVICE
        jz      @F
        mov     ebx,OFFSET dev_polyline_init
@@:     call    ebx
        ret


;/***************************************************************************
;*
;* PUBLIC ROUTINE  get_background_line_attributes  
;*
;* DESCRIPTION   = Initialize local variables with line color and style, and 
;*                 raster operation.                                         
;*                                                                           
;*                 Registers Destroyed:                                                                  
;*                       AX,BX                                                                           
;*                 Registers Preserved:        
;*                       CX,DX,SI,DI,BP,DS,ES  
;*
;* INPUT         = DS:DI = DDC  
;* OUTPUT        = AL = sd_flags  (ddc_npsd.sd_fb)  
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ALIGN 4
get_background_line_attributes:

        ddc?    edi

;/*
;** Get line color
;*/

        mov     al,BYTE PTR [edi].ddc_la.la_ba.ba_ipcBack.ipc_bClr

        mov     BYTE PTR CurLineColor,al

;/*
;** get line style
;*/

        mov     ebx,[edi].ddc_la.la_usType

;/*
;** The only thing that LineStyle is used for is special casing for
;** LINETYPE_SOLID and LINETYPE_ALTERNATE.  If LINETYPE_SOLID is the current
;** type, we won't ever come here.  If LINETYPE_ALTERNATE is the current line
;** type, we want the background to be drawn as LINETYPE_ALTERNATE too.  We
;** could set LineStyle to represent LINETYPE_SOLID if the foreground were
;** LINETYPE_INVISIBLE, but this is such a rare case that it is probably
;** unnecessary to worry about its efficiency.
;*/


        add     ebx,ebx
        movzx   eax,style_table[ebx]    ; a style mask from the table, and
        not     eax
        mov     dwStyleMask,eax         ; set the style flags.

        movzx   eax,WORD PTR [edi].ddc_la.la_bError ; AL=error, AH=mask position

        .errnz  LINE_ATTRS.la_bMask - LINE_ATTRS.la_bError - 1

        mov     dwStyleCounter,eax

;/*
;** Get the raster operation to use
;*/

        movzx   ebx,[edi].ddc_la.la_ba.ba_bkmix
        and     ebx,RASTER_OP_MASK      ;  (play it safe)
        mov     DrawModeIndex,ebx       ;  and save a copy
        ret

do_polylines    ENDP

        end
