;*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 =  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

Update_Bank     PROTO SYSCALL

        .list

        .MODEL FLAT

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

        .DATA

        PUBLIC  style_table
        PUBLIC  get_bm_info

        EXTERNDEF       Origin_bank:BYTE
        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

;/*
;** Binary raster ops
;** R2_BLACK                                       1 /*  0     */
;** R2_NOTMERGEPEN                                 2 /* DPon     */
;** R2_MASKNOTPEN                                  3 /* DPna     */
;** R2_NOTCOPYPEN                                  4 /* PN     */
;** R2_MASKPENNOT                                  5 /* PDna     */
;** R2_NOT                                         6 /* Dn     */
;** R2_XORPEN                                      7 /* DPx     */
;** R2_NOTMASKPEN                                  8 /* DPan     */
;** R2_MASKPEN                                     9 /* DPa     */
;** R2_NOTXORPEN                                   10 /* DPxn     */
;** R2_NOP                                         11 /* D     */
;** R2_MERGENOTPEN                                 12 /* DPno     */
;** R2_COPYPEN                                     13 /* P     */
;** R2_MERGEPENNOT                                 14 /* PDno     */
;** R2_MERGEPEN                                    15 /* DPo     */
;** R2_WHITE                                       16 /*  1     */
;*/


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


do_polylines    PROC SYSCALL USES esi edi,  fsPolyFlags:DWORD,
                                            cLines:DWORD,
                                            npaptl:DWORD,
                                            usFunN:WORD,
                                            fComFlags:WORD
 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,
         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,
         dwTmpActiveStyleCounter:DWORD,
         dwTmpActiveStyleMask:DWORD,
         cTmpPasses:DWORD,
         TmpCurLineColor:DWORD
 LOCAL                    Destination_device:byte
 LOCAL                    PreviousAddress:DWORD
 LOCAL                    OriginSegment:byte

        ddc?    edi

        DebugMsg <Pline>
        cld
        call    polyline_init

setup_polyline:

        mov     eax,fsPolyFlags
        mov     Flags,eax
        mov     eax,cLines
        mov     Count,eax
        mov     eax,npaptl
        mov     Points,eax
        mov     npddc,edi

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

;/*
;**!!! move this outside the batching loop !!!
;*/

        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

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

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

; load y clipping, normalize y coordinates relative to yStart

clipped_line:

        push    ebp          ;!!! we could just add/sub instead of push/pop !!!
        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

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

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

;/*
;**----------start of max metric changes
;*/

        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

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

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

;/*
;**----------end of max metric changes
;*/

;/*
;** 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
        jc      flip_it_style                     ; 2dy > dx -- half flip needed
        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

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

ignore_styles:

        mov     eax,esi
        add     eax,eax
        jc      flip_it                           ; 2dy > dx -- half flip needed
        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
;**!!! move outside batch loop !!!
;*/


        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

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   all_bitmaps          ;;CMVC_25279
        local   inc_eax

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 ?dda_huge

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

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

if ?dda_rotate
        push    eax
        push    ecx
        mov     eax,cbSideStepAddressDelta

        ;; * * * Monochrome Bitmap Support - MRC * * *
        cmp     cPlanes,1            ;;Check For Mono Bitmap
        jne     inc_eax
        ror     bl,1
        jnc     @F
inc_eax:
        inc     eax
@@:
        ;; * * * Monochrome Bitmap Support - MRC * * *

        cmp     Destination_device,1 ;;CMVC_25279
        je      @F                   ;;CMVC_25279
        call    Update_Bank
        jmp     all_bitmaps          ;;CMVC_25279
@@:                                  ;;CMVC_25279

        add     edi,eax              ;;CMVC_25279
all_bitmaps:                         ;;CMVC_25279
        pop     ecx
        pop     eax
else
        push    eax
        push    ecx
        mov     eax,cbSideStepAddressDelta

        cmp     Destination_device,1 ;;CMVC_25279
        je      @F                   ;;CMVC_25279
        call    Update_Bank
        jmp     all_bitmaps          ;;CMVC_25279
@@:                                  ;;CMVC_25279

        add     edi,eax              ;;CMVC_25279
all_bitmaps:                         ;;CMVC_25279
        pop     ecx
        pop     eax
endif ; ?dda_rotate

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

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
        adc     edx,ecx
        shr     edx,1                             ; / 2
        rcr     eax,1
        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
        adc     edx,edx
        inc     ecx
        sub     eax,ecx
        sbb     edx,0                             ; DX:AX = 2 dx - (rm + 1)
        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
        adc     edx,ecx
        shr     edx,1                             ; / 2
        rcr     eax,1
        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

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
        adc     edx,edx
        inc     ecx
        add     eax,ecx
        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

        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

not_huge_surface:

        mov     dwScan,ecx                        ; number of remaining scans

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

        mov     eax,cbScanSize
        cmp     Destination_device,1
        jne     target_is_device
        mul     edx
        jmp     row_offset_done
target_is_device:
        mul     dx
        push    eax
        mov     OriginSegment,dl
        mov     Origin_bank,dl
        call    set_bank_select
        pop     eax
row_offset_done:
        mov     ecx,ebx

;/*
;** need check bit depth to determine shift right or not.
;*/

        cmp     cPlanes,1
        jne     color_no_divide
        shr     ebx,3

color_no_divide:
        add     eax,ebx
        add     eax,pSurfaceStart
        mov     PreviousAddress,eax
        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
;JMW        mov     al,BYTE PTR CurLineColor
;JMW        mov     BYTE PTR TmpCurLineColor,al
        mov     ax,WORD PTR CurLineColor              ;JMW
        mov     WORD PTR TmpCurLineColor,ax           ;JMW

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

;/*
;** 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
        push    edx
        mov     dl,OriginSegment
        mov     Origin_bank,dl
        call    set_bank_select
        pop     edx
        jmp     do_another_pass

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

device_unsetup::

        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     ah,Code_rop_data_r[ebx]           ; Set DataRotate register value

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

        ret

        PUBLIC  device_one_pass

device_one_pass::
        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();
;* }
;*
;***************************************************************************/


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     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.


;/*
;** 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     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,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;
;*   }
;**************************************************************************/

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


bitmap_pass_per_plane::

;/*
;** determine the logical operation to do
;** only used by mono bitmap code
;*/
 
        mov     ebx,DrawModeIndex                ; get the raster op

;JMW        ror     BYTE PTR TmpCurLineColor,1      ; see if the color plane is set
;JMW        jnc     set_the_standard_proc
        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                   ;JMW

;JMW        add     ebx,ebx                 ; make a word ptr

        mov     eax,bm_and_xor_table[ebx] ; add index into table
        mov     dwBitmapROP,eax                   ; set up write logic
        mov     eax,1
        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
;*
;**************************************************************************/


dev_polyline_init:
ifdef   FIREWALLS
        DebugMsg <Pline_dev>
endif

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


bm_polyline_init::
ifdef   FIREWALLS
        DebugMsg <Pline_mem>
endif

        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,1
        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
;*
;**************************************************************************/


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 - 8
        ASSUME  ebx:PTR SURFACE

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

;The following line has been commented out since the SVGA makecolorsvalid
;routine will return a 1 or 0 for mono bitmaps instead of the vga
;standard of using the plane cnt + 1 bit. MRC

;MRC ???    shr     WORD PTR CurLineColor,8         ;JMW

@@:     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
        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

polyline_init::
        mov     ebx,[edi].ddc_npsd               ; get ptr to surface definition
        ASSUME  ebx:PTR SURFACE
        mov     cPlanes,BITS_PEL
        test    [ebx].sd_fb,SD_COLOR
        jnz     @F
        mov     cPlanes,1
@@:
        call    get_line_attributes

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

        mov     Destination_device,1
        mov     ebx,OFFSET bm_polyline_init
        test    [edi].ddc_fb,DDC_DEVICE
        jz      @F
        mov     ebx,OFFSET dev_polyline_init
        mov     Destination_device,0
@@:     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
;*
;**************************************************************************/


get_line_attributes:

        ddc?    edi

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

;JMW        mov     al,BYTE PTR [edi].ddc_la.la_ba.ba_ipc.ipc_bClr
;JMW        mov     BYTE PTR CurLineColor,al
        mov     ax,WORD PTR [edi].ddc_la.la_ba.ba_ipc.ipc_bClr     ;JMW
        mov     WORD PTR CurLineColor,ax                           ;JMW

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

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

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

get_background_line_attributes:

        ddc?    edi

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


        mov     ax,word ptr [edi].ddc_la.la_ba.ba_ipcBack.ipc_bClr
;JMW        mov     BYTE PTR CurLineColor,al
        mov     WORD PTR CurLineColor,ax                        ;JMW

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