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

;---------------------------Module-Header------------------------------;
; Module Name: fastline.asm
;
; This module draws solid lines using the line hardware of the S3.
; It handles entirely unclipped lines, or lines clipped to a single
; rectangle by using the S3's hardware clipping.
;
; All lines that have integer end-points are drawn directly using the
; line hardware, and any lines having fractional end-points and are
; less than 256 pels long are also drawn directly by the line hardware.
; (Fractional end-point lines require 4 more bits of precision from the
; DDA hardware than do integer end-point lines, and the S3 has but 13
; bits to use.  Lines longer than 255 pels are punted to the general
; purpose strip routines, which won't overflow the hardware).
;
; There are a number of ways unclipped lines can be sped up on the S3:
;
; 1) Optimize NT's GDI.  This isn't an option for everybody.
;
; 2) Use memory-mapped I/O.
;
; 3) If you can't use memory-mapped I/O, you can at least minimize
;    outs and ins because they are so painfully expensive.  One
;    way to do this is to amortize the cost of the in used for checking
;    the FIFO: do one in to make sure a large number of entries on
;    the FIFO are free (perhaps 6), then do the next 6 outs without
;    having to check the FIFO.
;
;    This will be a win on figures not requiring many outs (such as when
;    radial lines are drawn as with a rectangle), assuming the hardware
;    isn't always being overdriven.
;
; 4) In conjunction with 3) above, since short integer lines occur so
;    frequently with CAD programs, it would be beneficial to use short-
;    stroke vectors to do those lines, as they could reduce the number
;    of outs required.  Perhaps any line 10 pels or shorter could be
;    output by using an array lookup to determine the short stroke vectors
;    needed to represent that line.
;
; 5) Numerous other things I haven't thought of.
;
; Other improvements that should be done to make lines faster in general:
;
; 1) Simple clipping should be improved.  Currently, we simply set the
;    clip rectangle and let the hardware do everything.  We should at least
;    perform a trivial clip test to see if there's no chance the line
;    intersects the clip rectangle.
;
;    Also, for single lines it's expensive to do the 4 outs required to
;    set the S3's clip rectangle, draw the line, then do 4 outs to reset
;    the clip rectangle again.  In those cases, it would be faster to do the
;    clipping entirely in software.
;
; 2) This code can be enhanced to also handle styled lines, and to do
;    complex clipping more efficiently than the strips routines.
;
; 3) It is possible to derive the mathematical algorithm for drawing any
;    GIQ line in NT's 32 bit space using the limited bit precision of the
;    S3's hardware.  What this means is that absolutely any GIQ line can
;    be drawn precisely with at most two drawing commands to the line
;    hardware (and so the strips line drawing routine could be eliminated
;    entirely).  Just be careful of your math...
;
; 4) Numerous other things I haven't thought of.
;
; Copyright (c) 1992-1993 Microsoft Corporation
;-----------------------------------------------------------------------;

        .386

        .model  small,c

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

        .xlist
        include stdcall.inc             ;calling convention cmacros
        include i386\strucs.inc
        include i386\lines.inc
        .list

; Line coordinates are given in 28.4 fixed point format:

        F               equ 16
        FLOG2           equ 4

; The S3's hardware can have 13 bits of significance for the error and
; step terms:

        NUM_DDA_BITS    equ 13

; GIQ lines have to dedicate 4 bits to the fractional term, so the largest
; delta we can handle for GIQ lines is calculated as follows (scaled by F
; so we can do the test in GIQ coordinates), and remembering that one bit
; has to be used as a sign bit:

        MAX_GIQ_DELTA   equ (((1 shl (NUM_DDA_BITS - 5)) - 1) * F)

; The following values must match those in winddi.h!

        PD_BEGINSUBPATH equ 00000001h
        PD_ENDSUBPATH   equ 00000002h
        PD_RESETSTYLE   equ 00000004h
        PD_CLOSEFIGURE  equ 00000008h
        PD_BEZIERS      equ 00000010h

        PATHDATA struc

        pd_flags        dd  ?
        pd_count        dd  ?
        pd_pptfx        dd  ?

        PATHDATA ends

;-------------------------------------------------------------------------;
; I felt a compelling need to use 'ebp' as a 7th general register, and we
; have no nifty macros for dereferencing frame variables off 'esp'.  So
; with this structure I am rolling my own stack frame:

STATE_MEM_SIZE          equ 4           ;4 dwords
PROC_MEM_SIZE           equ 7           ;7 dwords

STACK_FRAME struc

; State variables (don't add/delete fields without modifying STATE_MEM_SIZE!)

sf_ulOurEbp             dd ?            ;useful for debugging
sf_ulOriginalEbx        dd ?
sf_ulOriginalEdi        dd ?
sf_ulOriginalEsi        dd ?

; Frame variables (feel free to add/delete fields):

sf_y0                   dd ?            ;GIQ variables
sf_y1                   dd ?
sf_x1                   dd ?
sf_ptlOrg               db (size POINTL) dup (?)
                                        ;our  origin for normalizing the line
sf_ptfxLast             db (size POINTL) dup (?)
                                        ;the most recent point
sf_ptfxStartFigure      db (size POINTL) dup (?)
                                        ;the figure's 1st point
sf_bMore                dd ?            ;more path records to get?
sf_pptfxEnd             dd ?            ;points to last point in record
sf_pptfx                dd ?            ;points to current point
sf_pd                   db (size PATHDATA) dup (?)
                                        ;pathdata structure
sf_ptfxStart            db (size POINTL) dup (?)
                                        ;temporary spot for saving start point
sf_ptfxEnd              db (size POINTL) dup (?)
                                        ;temporary spot for saving end point
sf_ulCmd                dd ?            ;S3 draw command
sf_cPels                dd ?            ;length of line in pels
sf_bSetCP               dd ?            ;1 if first line in figure, 0 otherwise
sf_ulLastLength         dd ?            ;last value set as LINE_MAX
sf_ulErrorTerm          dd ?            ;error term for line

; Procedure variables (don't add/delete fields without modifying
; PROC_MEM_SIZE!)

sf_ulOriginalEbp        dd ?
sf_ulOriginalReturn     dd ?
sf_ppdev                dd ?
sf_ppo                  dd ?
sf_prclClip             dd ?
sf_apfn                 dd ?
sf_flags                dd ?

STACK_FRAME ends

        .data

        EXTRNP  PATHOBJ_bEnum,8
        EXTRNP  bLines,36

        .code

        ROUND_X_DOWN            equ     01h
        ROUND_Y_DOWN            equ     02h
        ROUND_SLOPE_ONE         equ     04h
        ROUND_X_AND_Y_DOWN      equ     (ROUND_X_DOWN + ROUND_Y_DOWN)
        ROUND_X_DOWN_SLOPE_ONE  equ     (ROUND_X_DOWN + ROUND_SLOPE_ONE)
        ROUND_Y_DOWN_SLOPE_ONE  equ     (ROUND_Y_DOWN + ROUND_SLOPE_ONE)

;--------------------------------Macro----------------------------------;
; GIQ flags
;
; This macros computes the start pixel, the number of pixels to
; be lit, and the initial error term given a GIQ line.  The line must
; have already been normalized such that dM >= dN, dN >= 0.
;
; Input:   ebx - M0
;          ecx - N0
;          esi - dM
;          edi - dN
; Trashes:
;          eax, edx
;          [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
; Output:
;          [esp].sf_x1  - x-coordinate of last pixel (exclusive)
;          ebx - x-coordinate of first pixel
;          ecx - error term
;          esi - dM
;          edi - dN
;          ebp - y-coordinate of first pixel
;-----------------------------------------------------------------------;

GIQ     macro   flags
        local   compute_x1, compute_error_term

; We normalize our coordinate system so that if the start point is
; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):

        mov     eax,ebx
        mov     ebp,ecx
        sar     eax,FLOG2
        sar     ebp,FLOG2
        mov     [esp].sf_ptlOrg.ptl_x,eax
                                        ;ptlOrg.x = floor(M0 / F)
        mov     [esp].sf_ptlOrg.ptl_y,ebp
                                        ;ptlOrg.y = floor(N0 / F)

; Calculate the correct [esp].sf_x1:

        lea     ebp,[ecx + edi]         ;ebp = N1
        and     ebp,F - 1

    if (flags AND ROUND_X_DOWN)

    if (flags AND ROUND_SLOPE_ONE)
        lea     edx,[ebx + esi]
        and     edx,F - 1               ;edx = M1
        sub     edx,8
        cmp     edx,ebp                 ;cmp M1 - 8, N1
        jne     short compute_x1

        lea     edx,[ebx + esi]
        sar     edx,FLOG2
        jmp     short compute_error_term
    endif

compute_x1:

        cmp     ebp,1
        sbb     ebp,8                   ;N1 -= 8
    else
        sub     ebp,8                   ;N1 -= 8
    endif
        sbb     eax,eax
        xor     ebp,eax
        sub     ebp,eax                 ;N1 = ABS(N1)

        lea     edx,[ebx + esi]
        mov     eax,edx
        sar     edx,FLOG2
        and     eax,F - 1
        jz      short @f                ;special case for M1 == 0
        cmp     eax,ebp                 ;cmp M1, N1
        sbb     edx,-1                  ;edx is now one pixel past the actual
@@:                                     ; end coordinate (note that it hasn't
                                        ; been affected by the origin shift)

compute_error_term:

; ebx = M0
; ecx = N0
; edx = x1
; esi = dM
; edi = dN

        and     ecx,F - 1
        mov     [esp].sf_x1,edx         ;save x1

; Calculate our error term for x = 0.
;
; NOTE: Since this routine is used only for lines that are unclipped, we
;       are guaranteed by our screen size that the values will be far less
;       than 32 bits in significance, and so we don't worry about overflow.
;       If this is used for clipped lines, these multiplies will have to
;       be converted to give 64 bit results, because we can have 36 bits of
;       significance!


        lea     ebp,[ecx + 8]           ;ebp = N0 + 8
        mov     eax,esi
        imul    eax,ebp                 ;eax = dM * (N0 + 8)
        mov     ebp,edi

; We have to special case when M0 is 0 -- we know x0 will be zero.
; So we jump ahead a bit to a place where 'ebx' is assumed to contain
; x0 -- and it just so happens 'ebx' is zero in this case:

        and     ebx,F - 1
        jz      short @f
        imul    ebp,ebx                 ;ebp = dN * M0
        sub     eax,ebp

; Calculate the x-coordinate of the first pixel:

    if (flags AND ROUND_X_DOWN)

    if (flags AND ROUND_SLOPE_ONE)
        cmp     ecx,8
        sbb     ecx,-1
    endif

        cmp     ecx,1
        sbb     ecx,8                   ;N0 -= 8
    else
        sub     ecx,8                   ;N0 -= 8
    endif
        sbb     ebp,ebp
        xor     ecx,ebp
        sub     ecx,ebp                 ;N0 = ABS(N0)
        cmp     ebx,ecx
        sbb     ebx,ebx
        not     ebx                     ;ebx = -x0

; Now adjust the error term accordingly:

@@:
    if (flags AND ROUND_Y_DOWN)
        dec     eax
    endif
        sar     eax,FLOG2               ;eax = floor((N0 + 8) dM - M0 dN] / 16)

        mov     ecx,[esp].sf_ptlOrg.ptl_x
        mov     ebp,[esp].sf_ptlOrg.ptl_y

        sub     ecx,ebx                 ;ecx = ptlOrg.ptl_x + x0

        and     ebx,edi
        add     ebx,eax
        sub     ebx,esi                 ;ebx = dN * x0 + initial error - dM
        jl      short @f                ;if the error term >= 0, we have to
        sub     ebx,esi                 ; add 1 to the y position and subtract
        inc     ebp                     ; dM off again
@@:
        xchg    ebx,ecx

endm

;--------------------------------Macro----------------------------------;
; GIQR flags
;
; Same as above, except it handles flips about the line x = y.
;
; Input:   ebx - M0
;          ecx - N0
;          esi - dM
;          edi - dN
; Trashes:
;          eax, edx
;          [esp].sf_ptlOrg.ptl_x, [esp].sf_ptlOrg.ptl_y
; Output:
;          [esp].sf_y1  - y-coordinate of last pixel (exclusive)
;          ebx - x-coordinate of first pixel
;          ecx - error term
;          esi - dM
;          edi - dN
;          ebp - y-coordinate of first pixel
;-----------------------------------------------------------------------;

GIQR    macro   flags

; We normalize our coordinate system so that if the start point is
; (M0/F, N0/F), the origin is at (floor(M0/F), (N0/F)):

        mov     eax,ebx
        mov     ebp,ecx
        sar     eax,FLOG2
        sar     ebp,FLOG2
        mov     [esp].sf_ptlOrg.ptl_x,eax
                                        ;ptlOrg.x = floor(M0 / F)
        mov     [esp].sf_ptlOrg.ptl_y,ebp
                                        ;ptlOrg.y = floor(N0 / F)

; Calculate the correct [esp].sf_y1:

        lea     ebp,[ebx + esi]         ;ebp = M1
        and     ebp,F - 1

    if (flags AND ROUND_Y_DOWN)
        cmp     ebp,1
        sbb     ebp,8                   ;M1 -= 8
    else
        sub     ebp,8                   ;M1 -= 8
    endif
        sbb     eax,eax
        xor     ebp,eax
        sub     ebp,eax                 ;M1 = ABS(M1)

        lea     edx,[ecx + edi]
        mov     eax,edx
        sar     edx,FLOG2
        and     eax,F - 1
        jz      short @f                ;special case for N1 == 0
        cmp     eax,ebp                 ;cmp N1, M1
        sbb     edx,-1                  ;edx is now one pixel past the actual
@@:                                     ; end coordinate (note that it hasn't
                                        ; been affected by the origin shift)
        and     ebx,F - 1
        mov     [esp].sf_y1,edx

; Calculate our error term for y = 0.
;
; NOTE: Since this routine is used only for lines that are unclipped, we
;       are guaranteed by our screen size that the values will be far less
;       than 32 bits in significance, and so we don't worry about overflow.
;       If this is used for clipped lines, these multiplies will have to
;       be converted to give 64 bit results, because we can have 36 bits of
;       significance!

        lea     ebp,[ebx + 8]           ;ebp = M0 + 8
        mov     eax,edi
        imul    eax,ebp                 ;eax = dN * (M0 + 8)
        mov     ebp,esi

; We have to special case when N0 is 0 -- we know y0 will be zero.
; So we jump ahead a bit to a place where 'ecx' is assumed to contain
; y0 -- and it just so happens 'ecx' is zero in this case:

        and     ecx,F - 1
        jz      short @f
        imul    ebp,ecx                 ;ebp = dM * N0
        sub     eax,ebp

; Calculate the x-coordinate of the first pixel:

    if (flags AND ROUND_Y_DOWN)
        cmp     ebx,1
        sbb     ebx,8                   ;M0 -= 8
    else
        sub     ebx,8                   ;M0 -= 8
    endif
        sbb     ebp,ebp
        xor     ebx,ebp
        sub     ebx,ebp                 ;M0 = ABS(M0)
        cmp     ecx,ebx
        sbb     ecx,ecx
        not     ecx                     ;ecx = -y0

; Now adjust the error term accordingly:

@@:
    if (flags AND ROUND_X_DOWN)
        dec     eax
    endif
        sar     eax,FLOG2               ;eax = floor((M0 + 8) dN - N0 dM] / 16)

        mov     ebx,[esp].sf_ptlOrg.ptl_x
        mov     ebp,[esp].sf_ptlOrg.ptl_y

        sub     ebp,ecx                 ;ebp = ptlOrg.ptl_y + y0

        and     ecx,esi
        add     ecx,eax
        sub     ecx,edi                 ;ecx = dM * x0 + initial error - dN
        jl      short @f                ;if the error term >= 0, we have to
        sub     ecx,edi                 ; add 1 to the x position and subtract
        inc     ebx                     ; dN off again
@@:

endm

;---------------------------Public-Routine------------------------------;
; vFastLine(ppdev, ppo, prclClip, apfn, flags)
;
; Draws fast lines.  Or at least attempts to.
;
; Input:
;
;    ppdev     - PDEV pointer
;    ppo       - path
;    prclClip  - pointer to rectangle array for passing to 'bLines'
;    apfn      - pointer to strip routines for passing to 'bLines'
;    flags     - status flag for passing to 'bLines'
;
;-----------------------------------------------------------------------;

; NOTE: Don't go changing parameters without also changing STACK_FRAME!

cProc vFastLine,20,<     \
    uses esi edi ebx,    \
    ebp_ppdev:     ptr,  \
    ebp_ppo:       ptr,  \
    ebp_prclClip:  ptr,  \
    ebp_apfn:      ptr,  \
    ebp_flags:     dword >

; Leave room for our stack frame.
;
; NOTE: Don't add local variables here -- you can't reference them with
;       ebp anyway!  Add them to the STACK_FRAME structure.

    local aj[(size STACK_FRAME) - 4 * (STATE_MEM_SIZE + PROC_MEM_SIZE)]: byte

; We save 'ebp' on the stack (note that STACK_FRAME accounts for this push):

        push    ebp

        mov     [esp].sf_ulLastLength,-1;make sure first line in path resets
                                        ; LINE_MAX value

; Now get some path stuff:

next_record:

        mov     eax,[esp].sf_ppo
        lea     ebx,[esp].sf_pd
        cCall   PATHOBJ_bEnum,<eax,ebx>
        mov     [esp].sf_bMore,eax      ;save away return code for later

        mov     ebx,[esp].sf_pd.pd_count;if 0 points in record, get outta here
        or      ebx,ebx
        jz      check_for_closefigure

        lea     ebp,[8 * ebx - 8]
        add     ebp,[esp].sf_pd.pd_pptfx
        mov     [esp].sf_pptfxEnd,ebp   ;points to last point in record

        mov     ecx,[esp].sf_pd.pd_flags
        test    ecx,PD_BEGINSUBPATH
        jz      short continue_subpath

; Handle a new sub-path:

        mov     eax,[esp].sf_pd.pd_pptfx
        add     eax,8
        mov     [esp].sf_pptfx,eax

        mov     esi,[ebp].ptl_x         ;remember last point in case we have
        mov     edi,[ebp].ptl_y         ; to continue to another record
        mov     [esp].sf_ptfxLast.ptl_x,esi
        mov     [esp].sf_ptfxLast.ptl_y,edi

        mov     ebx,[eax - 8].ptl_x     ;load up current start and end point
        mov     ecx,[eax - 8].ptl_y
        mov     esi,[eax].ptl_x
        mov     edi,[eax].ptl_y
        mov     [esp].sf_ptfxStartFigure.ptl_x,ebx
        mov     [esp].sf_ptfxStartFigure.ptl_y,ecx

        mov     [esp].sf_bSetCP,1       ;this line is first in figure

        cmp     eax,[esp].sf_pptfxEnd   ;we have to be careful when the only
                                        ; point in the record is the start-
                                        ; figure point (pretty rare)
        jbe     new_line
        jmp     short next_record

continue_subpath:

; This record continues the path:

        mov     eax,[esp].sf_pd.pd_pptfx
        mov     ebx,[esp].sf_ptfxLast.ptl_x ;load up current start point
        mov     ecx,[esp].sf_ptfxLast.ptl_y

        mov     esi,[ebp].ptl_x         ;remember last point in case we have
        mov     edi,[ebp].ptl_y         ; to continue to another record
        mov     [esp].sf_ptfxLast.ptl_x,esi
        mov     [esp].sf_ptfxLast.ptl_y,edi

        mov     esi,[eax].ptl_x         ;load up current end point
        mov     edi,[eax].ptl_y
        mov     [esp].sf_pptfx,eax

        jmp     new_line

;/////////////////////////////////////////////////////////////////////
;// Next Line Stuff
;/////////////////////////////////////////////////////////////////////

handle_closefigure:
        mov     [esp].sf_pd.pd_flags,0
        mov     ebx,[esp].sf_ptfxLast.ptl_x
        mov     ecx,[esp].sf_ptfxLast.ptl_y
        mov     esi,[esp].sf_ptfxStartFigure.ptl_x
        mov     edi,[esp].sf_ptfxStartFigure.ptl_y

        jmp     new_line

; Before getting the next path record, see if we have to do a closefigure:

check_for_closefigure:
        test    [esp].sf_pd.pd_flags,PD_CLOSEFIGURE
        jnz     handle_closefigure
        mov     esi,[esp].sf_bMore
        or      esi,esi
        jnz     next_record

all_done:

        pop     ebp
        cRet    vFastLine

;/////////////////////////////////////////////////////////////////////
;// Output Integer Line
;/////////////////////////////////////////////////////////////////////

; ebx                  = x
; ecx                  = y
; esi                  = dx
; edi                  = dy
; [esp].sf_ulErrorTerm = incomplete error term (actual value is 'dx - bias')
; [esp].sf_ulCmd       = draw command

; NOTE: The port values retrieved from the PDEV are word values, and word
; moves have a one cycle penalty.  For this reason, the values should
; actually be defined as dwords in the PDEV...

        public  int_output_line
int_output_line:
        mov     ebp,[esp].sf_ppdev      ;ebp = ppdev
        cmp     [esp].sf_bSetCP,0
        je      short int_current_position_already_set

        mov     dx,[ebp].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_7_EMPTY        ;wait for 7 entries
        jnz     short @b

        mov     dx,[ebp].pdev_cur_x
        mov     eax,ebx
        out     dx,ax

        mov     dx,[ebp].pdev_cur_y
        mov     eax,ecx
        out     dx,ax

        mov     [esp].sf_bSetCP,0       ;the next integer line in this figure
                                        ; will have the CP set correctly

        jmp     short int_output_common

int_current_position_already_set:
        mov     dx,[ebp].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_5_EMPTY        ;wait for 5 entries
        jnz     short @b

        public  int_output_common
int_output_common:
        cmp     esi,[esp].sf_ulLastLength
        je      short @f

        mov     dx,[ebp].pdev_line_max
        mov     eax,esi
        out     dx,ax
        mov     [esp].sf_ulLastLength,eax
@@:
        mov     dx,[ebp].pdev_axstp
        mov     eax,edi
        out     dx,ax                   ;axial = dy

        mov     dx,[ebp].pdev_diastp
        sub     eax,esi
        out     dx,ax                   ;diag = dy - dx

        mov     edx,[esp].sf_ulErrorTerm
        sar     edx,1
        add     eax,edx
        mov     dx,[ebp].pdev_err_term
        out     dx,ax                   ;err = dy - dx + floor((dx - bias) / 2)

        mov     dx,[ebp].pdev_cmd
        mov     eax,[esp].sf_ulCmd
        out     dx,ax                   ;draw that puppy

;/////////////////////////////////////////////////////////////////////
;// Main Loop
;/////////////////////////////////////////////////////////////////////

        public  next_line
next_line:
        mov     eax,[esp].sf_pptfx
        cmp     eax,[esp].sf_pptfxEnd
        jae     check_for_closefigure

        mov     ebx,[eax].ptl_x
        mov     ecx,[eax].ptl_y
        mov     esi,[eax+8].ptl_x
        mov     edi,[eax+8].ptl_y
        add     eax,8
        mov     [esp].sf_pptfx,eax

        public  new_line
new_line:

; Octants are numbered as follows:
;
;        \ 5 | 6 /
;         \  |  /
;        4 \ | / 7
;           \ /
;       -----+-----
;           /|\
;        3 / | \ 0
;         /  |  \
;        / 2 | 1 \
;

; ebx = M0
; ecx = N0
; esi = M1 (dM)
; edi = N1 (dN)

        mov     [esp].sf_ptfxStart.ptl_x,ebx
        mov     [esp].sf_ptfxStart.ptl_y,ecx
        mov     [esp].sf_ptfxEnd.ptl_x,esi
        mov     [esp].sf_ptfxEnd.ptl_y,edi
                                        ;save points in case we have to punt
        mov     eax,ebx
        or      eax,ecx
        or      eax,esi
        or      eax,edi
        and     eax,F-1
        jnz     non_integer

;/////////////////////////////////////////////////////////////////////
;// Integer Lines
;/////////////////////////////////////////////////////////////////////

        sar     ebx,FLOG2               ;x0
        sar     ecx,FLOG2               ;y0
        sar     esi,FLOG2               ;x1 (soon to be dx)
        sar     edi,FLOG2               ;y1 (soon to be dy)

        sub     esi,ebx
        jl      int_2_3_4_5
        jz      int_radial_90_270
        sub     edi,ecx
        jl      int_6_7
        jz      int_radial_0
        cmp     esi,edi
        jl      int_1
        je      int_radial_315

int_0:
        lea     ebp,[esi - 1]
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X
        mov     [esp].sf_ulErrorTerm,ebp

        jmp     int_output_line

        public  int_1
int_1:
        xchg    esi,edi
        lea     ebp,[esi - 1]
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y
        mov     [esp].sf_ulErrorTerm,ebp

        jmp     int_output_line

        public  int_2_3_4_5
int_2_3_4_5:
        neg     esi
        sub     edi,ecx
        jl      int_4_5
        jz      int_radial_180
        cmp     esi,edi
        jl      int_2
        je      int_radial_225

        public  int_3
int_3:
        lea     ebp,[esi - 1]
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y
        mov     [esp].sf_ulErrorTerm,ebp

        jmp     int_output_line

        public  int_2
int_2:
        xchg    esi,edi
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y
        mov     [esp].sf_ulErrorTerm,esi

        jmp     int_output_line

        public  int_4_5
int_4_5:
        neg     edi
        cmp     esi,edi
        jl      int_5
        je      int_radial_135

int_4:
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD
        mov     [esp].sf_ulErrorTerm,esi

        jmp     int_output_line

        public  int_5
int_5:
        xchg    esi,edi
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y
        mov     [esp].sf_ulErrorTerm,esi

        jmp     int_output_line

        public  int_6_7
int_6_7:
        neg     edi
        cmp     esi,edi
        jg      int_7
        je      int_radial_45

int_6:
        xchg    esi,edi
        lea     ebp,[esi - 1]
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X
        mov     [esp].sf_ulErrorTerm,ebp

        jmp     int_output_line

        public  int_7
int_7:
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X
        mov     [esp].sf_ulErrorTerm,esi

        jmp     int_output_line

;/////////////////////////////////////////////////////////////////////
;// Lines In The 8 Cardinal Directions
;/////////////////////////////////////////////////////////////////////

        public  int_radial_45
int_radial_45:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_45
        jmp     short int_output_radial

        public  int_radial_135
int_radial_135:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_135
        jmp     short int_output_radial

        public  int_radial_225
int_radial_225:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_225
        jmp     short int_output_radial

        public  int_radial_315
int_radial_315:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_315
        jmp     short int_output_radial

        public  int_radial_90_270
int_radial_90_270:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_270
        mov     esi,edi
        sub     esi,ecx
        jg      short int_output_radial ;if top-to-bottom vertical line
        jz      next_line               ;if zero length line

        neg     esi
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_90
        jmp     short int_output_radial

        public  int_radial_180
int_radial_180:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_180
        jmp     short int_output_radial

int_radial_0:
        mov     ebp,DRAW_LINE + DRAW + DIR_TYPE_RADIAL + WRITE + \
                    LAST_PIXEL_OFF + MULTIPLE_PIXELS + DRAWING_DIRECTION_0

; ebx = x
; ecx = y
; esi = dx (length of radial line since it's normalized)
; ebp = drawing command

        public  int_output_radial
int_output_radial:
        mov     edi,[esp].sf_ppdev
        cmp     [esp].sf_bSetCP,0
        je      short int_radial_continue_figure

        mov     dx,[edi].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_4_EMPTY
        jnz     short @b

        mov     dx,[edi].pdev_cur_x
        mov     eax,ebx
        out     dx,ax

        mov     dx,[edi].pdev_cur_y
        mov     eax,ecx
        out     dx,ax

        mov     [esp].sf_bSetCP,0       ;the next integer line in this figure
                                        ; will have the CP set correctly

        cmp     esi,[esp].sf_ulLastLength
        je      short @f

        mov     dx,[edi].pdev_line_max
        mov     eax,esi
        out     dx,ax
        mov     [esp].sf_ulLastLength,eax
@@:
        mov     dx,[edi].pdev_cmd
        mov     eax,ebp
        out     dx,ax
        jmp     next_line

; Jump to here if we don't have to update the current position first:

        public  int_radial_continue_figure
int_radial_continue_figure:
        cmp     esi,[esp].sf_ulLastLength
        je      short int_radial_skip_length

        mov     dx,[edi].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_2_EMPTY
        jnz     short @b

        mov     dx,[edi].pdev_line_max
        mov     eax,esi
        out     dx,ax
        mov     [esp].sf_ulLastLength,eax

        mov     dx,[edi].pdev_cmd
        mov     eax,ebp
        out     dx,ax
        jmp     next_line

; Jump to here if we don't have to update the current position or the
; line length variable:

        public  int_radial_skip_length
int_radial_skip_length:
        mov     dx,[edi].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_1_EMPTY
        jnz     short @b

        mov     dx,[edi].pdev_cmd
        mov     eax,ebp
        out     dx,ax
        jmp     next_line

;/////////////////////////////////////////////////////////////////////
;// Non-Integer Lines
;/////////////////////////////////////////////////////////////////////

        public  non_integer
non_integer:
        sub     esi,ebx
        jl      non_int_2_3_4_5
        sub     edi,ecx
        jl      non_int_6_7
        cmp     esi,edi
        jl      non_int_1
        je      non_int_0_slope_one

non_int_0:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_X_AND_Y_DOWN

non_int_0_common:
        add     ecx,edi                 ;err += dN
        mov     [esp].sf_ulErrorTerm,ecx

        mov     eax,[esp].sf_x1
        sub     eax,ebx
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X

        public  non_int_x_major
non_int_x_major:
        mov     ecx,[esp].sf_ppdev
        mov     dx,[ecx].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_7_EMPTY        ;wait for 7 entries
        jnz     short @b

; Fractional end-point lines aren't usually going to have the current
; position already set correctly, so we explicitly set the CP each time:

        mov     dx,[ecx].pdev_cur_x
        mov     eax,ebx
        out     dx,ax                   ;x0

        mov     dx,[ecx].pdev_cur_y
        mov     eax,ebp
        out     dx,ax                   ;y0

        mov     [esp].sf_bSetCP,1       ;the next integer line in this figure
                                        ; will not necessarily have the CP set
                                        ; correctly

        mov     eax,[esp].sf_cPels
        cmp     eax,[esp].sf_ulLastLength
        je      short @f

        mov     dx,[ecx].pdev_line_max
        out     dx,ax                   ;length = cPels
        mov     [esp].sf_ulLastLength,eax
@@:
        mov     dx,[ecx].pdev_axstp
        mov     eax,edi
        out     dx,ax                   ;axial = dN

        mov     dx,[ecx].pdev_diastp
        sub     eax,esi
        out     dx,ax                   ;diag = dN - dM

        mov     dx,[ecx].pdev_err_term
        mov     eax,[esp].sf_ulErrorTerm
        out     dx,ax                   ;error term

        mov     dx,[ecx].pdev_cmd
        mov     eax,[esp].sf_ulCmd
        out     dx,ax                   ;output it

        jmp     next_line

; Lines of slope one have a special rounding rule: when the line
; runs exactly half way between two pixels, the upper or right pel
; is illuminated.  This translates into x=1/2 rounding up in value,
; and y=1/2 rounding down:

        public  non_int_0_slope_one
non_int_0_slope_one:
        or      esi,esi
        jz      next_line               ;quick check for a zero length GIQ line

        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_Y_DOWN_SLOPE_ONE
        jmp     non_int_0_common

        public  non_int_1
non_int_1:
        cmp     edi,MAX_GIQ_DELTA
        jg      punt_line

        GIQR    ROUND_X_AND_Y_DOWN
        add     ecx,esi                 ;err += dM
        mov     [esp].sf_ulErrorTerm,ecx

        mov     eax,[esp].sf_y1
        sub     eax,ebp
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + PLUS_X + MAJOR_Y

        public  non_int_y_major
non_int_y_major:
        mov     ecx,[esp].sf_ppdev
        mov     dx,[ecx].pdev_gp_stat
@@:     in      ax,dx
        and     eax,FIFO_7_EMPTY        ;wait for 7 entries
        jnz     short @b

; Fractional end-point lines aren't usually going to have the current
; position already set correctly, so we explicitly set the CP each time:

        mov     dx,[ecx].pdev_cur_x
        mov     eax,ebx
        out     dx,ax                   ;x0

        mov     dx,[ecx].pdev_cur_y
        mov     eax,ebp
        out     dx,ax                   ;y0

        mov     [esp].sf_bSetCP,1       ;the next integer line in this figure
                                        ; will not necessarily have the CP set
                                        ; correctly

        mov     eax,[esp].sf_cPels
        cmp     eax,[esp].sf_ulLastLength
        je      short @f

        mov     dx,[ecx].pdev_line_max
        out     dx,ax                   ;length = cPels
        mov     [esp].sf_ulLastLength,eax
@@:
        mov     dx,[ecx].pdev_axstp
        mov     eax,esi
        out     dx,ax                   ;axial = dM

        mov     dx,[ecx].pdev_diastp
        sub     eax,edi
        out     dx,ax                   ;diag = dM - dN

        mov     dx,[ecx].pdev_err_term
        mov     eax,[esp].sf_ulErrorTerm
        out     dx,ax                   ;error term

        mov     dx,[ecx].pdev_cmd
        mov     eax,[esp].sf_ulCmd
        out     dx,ax                   ;output it

        jmp     next_line

        public  non_int_2_3_4_5
non_int_2_3_4_5:
        neg     esi                     ;dM = -dM (now positive)
        neg     ebx                     ;M0 = -M0
        sub     edi,ecx
        jl      non_int_4_5
        cmp     esi,edi
        jl      non_int_2

non_int_3:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_Y_DOWN
        add     ecx,edi                 ;err += dN
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebx                     ;untransform x0

        mov     eax,[esp].sf_x1
        add     eax,ebx
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y

        jmp     non_int_x_major

        public  non_int_2
non_int_2:
        cmp     edi,MAX_GIQ_DELTA
        jg      punt_line

        GIQR    ROUND_Y_DOWN
        add     ecx,esi                 ;err += dM
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebx                     ;untransform x0

        mov     eax,[esp].sf_y1
        sub     eax,ebp
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_Y + MAJOR_Y

        jmp     non_int_y_major

        public  non_int_4_5
non_int_4_5:
        neg     edi                     ;dN = -dN (now positive)
        neg     ecx                     ;N0 = -N0
        cmp     esi,edi
        jl      non_int_5
        je      non_int_4_slope_one

        public  non_int_4
non_int_4:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     0

non_int_4_common:
        add     ecx,edi                 ;err += dN
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebx                     ;untransform x0
        neg     ebp                     ;untransform y0

        mov     eax,[esp].sf_x1
        add     eax,ebx
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD

        jmp     non_int_x_major

; Lines of slope one have a special rounding rule.

        public  non_int_4_slope_one
non_int_4_slope_one:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_X_DOWN_SLOPE_ONE
        jmp     non_int_4_common

        public  non_int_5
non_int_5:
        cmp     edi,MAX_GIQ_DELTA
        jg      punt_line

        GIQR    0
        add     ecx,esi                 ;err += dM
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebx                     ;untransform x0
        neg     ebp                     ;untransform y0

        mov     eax,[esp].sf_y1
        add     eax,ebp
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y

        jmp     non_int_y_major

        public  non_int_6_7
non_int_6_7:
        neg     edi                     ;dN = -dN (now positive)
        neg     ecx                     ;M0 = -M0
        cmp     esi,edi
        je      non_int_7_slope_one
        jg      non_int_7

        public  non_int_6
non_int_6:
        cmp     edi,MAX_GIQ_DELTA
        jg      punt_line

        GIQR    ROUND_X_DOWN
        add     ecx,esi                 ;err += dM
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebp                     ;untransform y0

        mov     eax,[esp].sf_y1
        add     eax,ebp
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + MAJOR_Y + PLUS_X

        jmp     non_int_y_major

        public  non_int_7
non_int_7:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_X_DOWN

non_int_7_common:
        add     ecx,edi                 ;err += dN
        mov     [esp].sf_ulErrorTerm,ecx

        neg     ebp                     ;untransform y0

        mov     eax,[esp].sf_x1
        sub     eax,ebx
        jle     next_line
        mov     [esp].sf_cPels,eax
        mov     [esp].sf_ulCmd,DEFAULT_DRAW_CMD + PLUS_X

        jmp     non_int_x_major

        public  non_int_7_slope_one
non_int_7_slope_one:
        cmp     esi,MAX_GIQ_DELTA
        jg      punt_line

        GIQ     ROUND_X_DOWN_SLOPE_ONE
        jmp     non_int_7_common

;/////////////////////////////////////////////////////////////////////
;// Punt Line
;/////////////////////////////////////////////////////////////////////

; If the line is too long, we punt to our strip drawing routine.

        public  punt_line
punt_line:
        mov     esi,esp
        lea     eax,[esp].sf_ptfxStart
        lea     ebx,[esp].sf_ptfxEnd

        cCall   bLines,<[esi].sf_ppdev, eax, ebx, 0, 1, 0, \
                        [esi].sf_prclClip, [esi].sf_apfn, [esi].sf_flags>

        mov     [esp].sf_bSetCP,1       ;Always reset CP after punting
        mov     [esp].sf_ulLastLength,-1;Always reset line length after punting

        jmp     next_line

endProc vFastLine

        end

unix.superglobalmegacorp.com

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