;-----------------------------------------------------------
;
; MXLN.ASM - Line function
; Copyright (c) 1993,1994 by Alessandro Scotti
;
;-----------------------------------------------------------
WARN    PRO
NOWARN  RES
INCLUDE MODEX.DEF

PUBLIC  mxLine

MX_TEXT SEGMENT USE16 PARA PUBLIC 'CODE'
        ASSUME cs:MX_TEXT, ds:NOTHING, es:NOTHING

EXTRN   mx_BytesPerLine : WORD
EXTRN   mx_VideoSegment : WORD

EXTRN   mx_ClipX1       : WORD
EXTRN   mx_ClipY1       : WORD
EXTRN   mx_ClipX2       : WORD
EXTRN   mx_ClipY2       : WORD

tblDrawFunc     LABEL   WORD
        DW      subWidthMove
        DW      subHeightMove
        DW      subWidthOp
        DW      subHeightOp

;-----------------------------------------------------------
;
; Draws a line from (X1,Y1) to (X2,Y2) using the Bresenham
; algorithm.
;
; Input:
;       X1, Y1  = start point
;       X2, Y2  = end point
;       Color   = line color
;       Op      = raster operator
; Output:
;       none
;
; Note: the end point (X2,Y2) *IS* drawed. I don't like this very much
; but clipping is much simpler.
;
mxLine  PROC FAR
        ARG     Op:WORD,        \
                Color:WORD,     \
                Y2:WORD,        \
                X2:WORD,        \
                Y1:WORD,        \
                X1:WORD         = ARG_SIZE
        LOCAL   Width:WORD,     \
                Height:WORD,    \
                ErrorAdd:WORD,  \
                ErrorSub:WORD,  \
                DeltaX:WORD,    \
                DeltaY:WORD,    \
                P1:BYTE,        \
                P2:BYTE,        \
                WritePlane:BYTE = AUTO_SIZE
        .enter  AUTO_SIZE
        .push   ds, si, di
        ASSUME  ds:NOTHING

        mov     ax, [X1]
        mov     bx, [Y1]
        mov     cx, [X2]
        mov     dx, [Y2]
        call    subClipLine
        jc      @@Exit                  ; Line is full clipped

; Get width
        mov     si, cx
        xchg    ax, si                  ; SI = X1, AX = X2
        sub     ax, si
        jge     @@1
; Swap points, we want X1 < X2
        xchg    si, cx                  ; Swap X1 and X2
        xchg    bx, dx                  ; Swap Y1 and Y2
        neg     ax
@@1:
        mov     [Width], ax

; Get height
        mov     cx, [mx_BytesPerLine]   ; We don't need X2 anymore
        mov     ax, dx
        sub     ax, bx
        jge     @@2
        neg     cx                      ; Move from bottom to top
        neg     ax                      ; Get absolute value of AX
@@2:
        mov     [Height], ax
        mov     [DeltaY], cx

; Get pixel address and write plane
        mov     ax, bx
        mul     [mx_BytesPerLine]
        mov     cx, si                  ; CX = X1
        shr     si, 1
        shr     si, 1
        add     si, ax                  ; SI = pixel offset
        and     cl, 03h
        mov     ax, 1102h
        shl     ah, cl
        mov     [WritePlane], ah
        mov     dx, TS
        out     dx, ax                  ; Set write plane
        mov     ax, [mx_VideoSegment]
        mov     ds, ax                  ; DS:SI points to (X1,Y1)

; Select the function to handle the drawing loop
        xor     bx, bx
        mov     al, BYTE PTR [Op]
        cmp     al, OP_MOVE
        je      @@3
        and     al, 03h
        shl     al, 1
        shl     al, 1
        shl     al, 1
        mov     ah, al
        mov     al, 03h
        mov     dx, GDC
        out     dx, ax                  ; Set logical function
        inc     bx
        inc     bx
@@3:
        mov     ax, [Width]
        mov     cx, [Height]
; Horizontal, vertical and diagonal lines are not optimized yet
        cmp     ax, cx
        jae     @@4
        inc     bx
@@4:
        shl     bx, 1
        call    tblDrawFunc[bx]

; Reset logical function if needed
        cmp     BYTE PTR [Op], OP_MOVE
        je      @@Exit
        mov     ax, 0003h
        mov     dx, GDC
        out     dx, ax

@@Exit:
        xor     ax, ax
        .pop    ds, si, di
        .leave  ARG_SIZE

;-----------------------------------------------------------
;
; Checks the coordinates of a line against the active
; clip region.
; Uses a variation of the Cohen-Sutherland algorithm developed
; by Victor Duvanenko.
;
; Input:
;       AX, BX  = X1, Y1
;       CX, DX  = X2, Y2
; Output:
;       CF      = set if line is full clipped
;       AX, BX  = clipped X1, Y1
;       CX, DX  = clipped X2, Y2
; Note:
;       destroys SI, DI
;
subClipLine     PROC    NEAR
        mov     di, ax                  ; Copy X1 to DI and free AX
        mov     si, dx                  ; Copy Y2 to SI and free DX
; Compute clip codes for point (X2,Y2)=(CX,SI)
        xor     al, al
@@P2X1: cmp     cx, [mx_ClipX1]
        jge     @@P2X2
        or      al, 1
@@P2X2: cmp     cx, [mx_ClipX2]
        jle     @@P2Y1
        or      al, 2
@@P2Y1: cmp     si, [mx_ClipY1]
        jge     @@P2Y2
        or      al, 4
@@P2Y2: cmp     si, [mx_ClipY2]
        jle     @@P2XY
        or      al, 8
@@P2XY: mov     [P2], al
; Compute clip codes for point (X1,Y1)=(DI,BX)
        xor     al, al
@@P1X1: cmp     di, [mx_ClipX1]
        jge     @@P1X2
        or      al, 1
@@P1X2: cmp     di, [mx_ClipX2]
        jle     @@P1Y1
        or      al, 2
@@P1Y1: cmp     bx, [mx_ClipY1]
        jge     @@P1Y2
        or      al, 4
@@P1Y2: cmp     bx, [mx_ClipY2]
        jle     @@P1XY
        or      al, 8
@@P1XY: mov     [P1], al
; Check codes for trivial cases
        mov     ah, [P2]
        test    al, ah                  ; Is line invisible?
        jnz     @@FullClip              ; Yes, exit
        or      ah, al                  ; Both points clipped?
        jz      @@Done                  ; Yes, exit
; Calculate deltas
        mov     ax, cx
        sub     ax, di
        mov     [DeltaX], ax
        mov     ax, si
        sub     ax, bx
        mov     [DeltaY], ax
        mov     al, [P1]                ; Init clipping code
; Clipping loop
@@ClipLoop:
        test    al, al                  ; Is first point clipped?
        jnz     @@ClipX1                ; No, continue
        xchg    cx, di                  ; Swap points...
        xchg    bx, si
        xchg    al, [P2]                ; ...and codes
; Clip left: Y1 = Y1 + DeltaY*(mx_ClipX1-X1)/DeltaX
@@ClipX1:
        test    al, 1
        jz      @@ClipX2
        mov     ax, [mx_ClipX1]
        sub     ax, di
        mov     di, [mx_ClipX1]
        jmp     @@ClipX1X2
; Clip right: Y1 = Y1 + DeltaY*(mx_ClipX2-X1)/DeltaX
@@ClipX2:
        test    al, 2
        jz      @@ClipY1
        mov     ax, [mx_ClipX2]
        sub     ax, di
        mov     di, [mx_ClipX2]
@@ClipX1X2:
        imul    [DeltaY]
        idiv    [DeltaX]
        add     bx, ax
        mov     al, 8
        cmp     bx, [mx_ClipY2]
        jg      @@CheckLoop
        mov     al, 4
        cmp     bx, [mx_ClipY1]
        jl      @@CheckLoop
        xor     al, al
        jmp     @@CheckLoop
; Clip top: X1 = X1 + DeltaX*(mx_ClipY1-Y1)/DeltaY
@@ClipY1:
        test    al, 4
        jz      @@ClipY2
        mov     ax, [mx_ClipY1]
        sub     ax, bx
        mov     bx, [mx_ClipY1]
        jmp     @@ClipY1Y2
; Clip bottom: X1 = X1 + DeltaX*(mx_ClipY2-Y1)/DeltaY
@@ClipY2:
        mov     ax, [mx_ClipY2]
        sub     ax, bx
        mov     bx, [mx_ClipY2]
@@ClipY1Y2:
        imul    [DeltaX]
        idiv    [DeltaY]
        add     di, ax
        mov     al, 1
        cmp     di, [mx_ClipX1]
        jl      @@CheckLoop
        mov     al, 2
        cmp     di, [mx_ClipX2]
        jg      @@CheckLoop
        xor     al, al
@@CheckLoop:
        mov     ah, [P2]
        test    al, ah
        jnz     @@FullClip
        or      ah, al
        jnz     @@ClipLoop

@@Done:
        mov     ax, di
        mov     dx, si
        clc
        ret
@@FullClip:
        stc
        ret
subClipLine     ENDP

; Called when Width >= Height and Op = OP_MOVE
subWidthMove    PROC NEAR
        mov     di, ax
        neg     di                      ; Initialize error term
        shl     cx, 1
        mov     [ErrorAdd], cx
        mov     cx, ax
        shl     ax, 1
        mov     [ErrorSub], ax
        mov     al, 02h
        mov     ah, [WritePlane]
        mov     bl, BYTE PTR [Color]
        mov     dx, TS
        inc     cx
@@Loop:
        mov     ds:[si], bl
        dec     cx
        jz      @@Exit
        rol     ah, 1
        adc     si, 0
        out     dx, ax
        add     di, [ErrorAdd]
        jl      @@Loop
        add     si, [DeltaY]
        sub     di, [ErrorSub]
        jmp     @@Loop
@@Exit:
        ret
subWidthMove    ENDP

; Called when Width < Height and Op = OP_MOVE
subHeightMove   PROC NEAR
        mov     di, cx
        neg     di                      ; Initialize error term
        shl     ax, 1
        mov     [ErrorAdd], ax
        mov     ax, cx
        shl     ax, 1
        mov     [ErrorSub], ax
        mov     bl, BYTE PTR [Color]
        mov     ah, [WritePlane]
        mov     al, 02h
        mov     dx, TS
        inc     cx
@@Loop:
        mov     ds:[si], bl
        dec     cx
        jz      @@Exit
        add     si, [DeltaY]
        add     di, [ErrorAdd]
        jl      @@Loop
        rol     ah, 1                   ; Next write plane
        adc     si, 0                   ; Bump video offset if plane overflows
        out     dx, ax
        sub     di, [ErrorSub]          ; Adjust error down
        jmp     @@Loop
@@Exit:
        ret
subHeightMove   ENDP

; Called when Width >= Height and Op <> OP_MOVE
subWidthOp      PROC NEAR
        mov     di, ax
        neg     di                      ; Initialize error term
        shl     cx, 1
        mov     [ErrorAdd], cx
        mov     cx, ax
        shl     ax, 1
        mov     [ErrorSub], ax
        mov     al, 02h
        mov     ah, [WritePlane]
        mov     bl, BYTE PTR [Color]
        mov     dx, TS
        inc     cx
@@Loop:
        mov     bh, ds:[si]             ; Latch data
        mov     ds:[si], bl
        dec     cx
        jz      @@Exit
        rol     ah, 1
        adc     si, 0
        out     dx, ax
        add     di, [ErrorAdd]
        jl      @@Loop
        add     si, [DeltaY]
        sub     di, [ErrorSub]
        jmp     @@Loop
@@Exit:
        ret
subWidthOp      ENDP

; Called when Width < Height and Op <> OP_MOVE
subHeightOp     PROC NEAR
        mov     di, cx
        neg     di                      ; Initialize error term
        shl     ax, 1
        mov     [ErrorAdd], ax
        mov     ax, cx
        shl     ax, 1
        mov     [ErrorSub], ax
        mov     bl, BYTE PTR [Color]
        mov     ah, [WritePlane]
        mov     al, 02h
        mov     dx, TS
        inc     cx
@@Loop:
        mov     bh, ds:[si]
        mov     ds:[si], bl
        dec     cx
        jz      @@Exit
        add     si, [DeltaY]
        add     di, [ErrorAdd]
        jl      @@Loop
        rol     ah, 1                   ; Next write plane
        adc     si, 0                   ; Bump video offset if plane overflows
        out     dx, ax
        sub     di, [ErrorSub]          ; Adjust error down
        jmp     @@Loop
@@Exit:
        ret
subHeightOp     ENDP

mxLine  ENDP

MX_TEXT         ENDS
END
