
;************************************************************************
; Line (x0, y0, x1, y1,c ): draw line from (x0,y0) to (x1,y1)           *
;               with a color (c).                                       *
;               This routine is divided into three parts. Horizontal    *
;               lines are done in the first part, vertical lines in     *
;               second part, and rest in the third part.  The lines     *
;               in the third part are done using the Bresenhams         *
;               algorithm.                                              *
; entry : sp + 2 = x0                                                   *
;         sp + 4 = y0                                                   *
;         sp + 6 = x1                                                   *
;         sp + 8 = y1                                                   *
;         sp + 10= color                                                *
;************************************************************************

X0      EQU     [BP+4]
Y0      EQU     [BP+6]
X1      EQU     [BP+8]
Y1      EQU     [BP+10]
Color   EQU     [BP+12]

        PUBLIC  _Line

_Line   PROC    NEAR
        PUSH    BP
        MOV     BP,SP
        SUB     SP,6                    ;Declare three local variables

        PUSH    DI
        PUSH    SI
        PUSH    DS
        PUSH    ES

        MOV     AX,X0                   ; make sure that x1 >= x0
        MOV     CX,X1
        CMP     CX,AX
        JGE     Get_Offset
        MOV     BX,Y0
        MOV     DX,Y1
        MOV     X0,CX
        MOV     Y0,DX
        MOV     X1,AX
        MOV     Y1,BX

;-------COMPUTE ADDRESS AND MASK FOR FIRST PIXEL -------------
Get_Offset:
        ;--- Compute offset and save on stack
        MOV     CL,4                    ; offset = 80 * y + x/8
        MOV     AX,Y0
        SHL     AX,CL
        MOV     BX,AX
        SHL     AX,1
        SHL     AX,1
        ADD     AX,BX
        MOV     BX,X0                   ; + x/8
        MOV     CL,3
        SHR     BX,CL
        ADD     AX,BX
        PUSH    AX                      ; save offset on stack, later pop to DI

        ;--- Compute mask and save on the stack

        MOV     CX,X0                   ; compute which bit (x mod 8) to modify
        AND     CL,7
        MOV     BX,80h
        SHR     BX,CL
        PUSH    BX                      ; save mask on stack, later pop to SI
        MOV     DX,03CEh                ; enable only the bit(within a byte)
        MOV     AL,08H                  ; to be changed
        OUT     DX,AL
        INC     DX
        MOV     AL,BL                   ; ... reg-bx has the correct bit
        OUT     DX,AL

        ;--- Load set/reset registers with current color

        MOV     DX,03CEh                ; move color into reset register
        XOR     AL,AL
        OUT     DX,AL
        INC     DX
        MOV     AX,Color
        OUT     DX,AL
        DEC     DX                      ; enable use of reset register
        MOV     AL,1
        OUT     DX,AL
        INC     DX
        MOV     AL,0FH
        OUT     DX,AL
        MOV     DX,03C4h                ; enable all four planes for writting
        MOV     AL,2
        OUT     DX,AL
        INC     DX
        MOV     AL,0FH
        OUT     DX,AL

        ;--- Load segment register

        MOV     DX,0A000h
        MOV     DS,DX
        MOV     ES,DX

;-------COMPUTE  DX AND DY ----------------------------------------
;       DETERMINE IF HORIZONTAL, VERTICAL OR DIAGONNAL LINE

        MOV     AX,80                   ; set raster increment
        MOV     [bp-6],AX
        MOV     SI,X1                   ; compute dx           reg-si
        SUB     SI,X0
        MOV     DI,Y1                   ; compute dy           reg-di
        SUB     DI,Y0
        JGE     DyIsPos
        NEG     AX
        MOV     [bp-6],AX
        NEG     DI
DyIsPos:
        CMP     SI,0                    ; jump according to type of line
        JZ      Vert
        CMP     DI,0
        JZ      Horiz
        JMP     Diag

;-------GENERATE A VERTICAL LINE -----------------

Vert:   MOV     cx,di                   ; set up counter
        INC     cx
        MOV     bx,[bp-6]

        POP     SI                      ; fetch mask
        MOV     DX,03CEh                ; set mask
        MOV     AL,08h
        OUT     DX,AL
        INC     DX
        MOV     AX,SI
        OUT     DX,AL

        POP     DI                      ; fetch offset
LoopVert:
        MOV     AL,[DI]                 ; latch data(to preserve other 7 bits i
        MOV     [DI],AL                 ; write new data (only one bit will be
        ADD     DI,BX                   ; update offset
        LOOP    LoopVert
        JMP     LineDone

;-------GENERATE A HORIZONTAL LINE ---------------

Horiz:  MOV     CX,SI                   ; set counter of pixels
        POP     SI                      ; fetch mask
        POP     DI                      ; fetch offset

        ;---    draw pixels from the leading partial byte

        MOV     AX,X0
        AND     AX,07h                  ; check for partial byte
        JZ      FullBytes
        MOV     BX,0FFh                 ; compute the mask
        PUSH    CX
        MOV     CX,AX
        SHR     BX,CL
        POP     CX
        ADD     CX,AX                   ; update counter
        SUB     CX,08h
        JGE     MaskSet                 ; modify mask if only one byte
        NEG     CX
        SHR     BX,CL
        SHL     BX,CL
        XOR     CX,CX                   ; restore counter
MaskSet:
        MOV     DX,03CEh                ; set the mask
        MOV     AL,08h
        OUT     DX,AL
        INC     DX
        MOV     AL,BL
        OUT     DX,AL
        MOV     AH,[DI]                 ; latch data
        MOV     [DI],AL                 ; write new data
        INC     DI                      ; update offset

        ;---    draw pixels from the middle complete bytes
FullBytes:                              ;
        MOV     BX,CX                   ; check if any bytes to set
        CMP     DX,8
        JL      TrailBytes
        SHR     CX,1                    ; compute count
        SHR     CX,1
        SHR     CX,1
        MOV     DX,03CEh                ; set the mask
        MOV     AL,08h
        OUT     DX,AL
        INC     DX
        MOV     AL,0FFh
        OUT     DX,AL

        REP     STOSB                   ; fill complete bytes

        ;---    draw pixels from the trailing partial byte
TrailBytes:
        AND     BX,07h
        JZ      HorizDone
        MOV     AX,0FFFFh                ; compute mask
        MOV     CX,BX
        SHR     AX,CL
        XOR     AH,0FFh                  ; set the mask
        MOV     DX,03CEh
        MOV     AL,08h
        OUT     DX,AL
        INC     DX
        MOV     AL,AH
        OUT     DX,AL
        MOV     AL,[DI]                  ; latch data
        MOV     [DI],AL                  ; set new data
HorizDone:
        JMP     LineDone

;----------------------- GENERATE A DIAGONAL LINE -----------------

        ;--- figure out which quarter does the line lie in

Diag:   CMP     SI,DI                   ; Is dy > dx
        JLE     oct12                   ; ...Yes, do processing in octants
                                        ;    1 and 2
        ; Compute constants for octant zero and three
        ; This is where x is the major direction and y is minor

oct03:  MOV     CX,SI                   ; set counter to dx+1
        INC     CX
        SAL     DI,1                    ; d1 = dy*2             reg-di
        MOV     BX,DI                   ; d  = dy*2-dx          reg-bx
        SUB     BX,SI
        NEG     SI                      ; d2 = dy*2-dx-dx       reg-si
        ADD     SI,BX

        MOV     [bp-2],di               ; save d1
        MOV     [bp-4],si               ; save d2
        MOV     DX,03CEh                ; select BIT MASK register
        MOV     AL,08h
        OUT     DX,AL
        INC     DX
        POP     AX                      ; fetch mask
        POP     DI                      ; fetch address

        ;-------------- GENERATE LINE IN THE OCTANT ZERO AND THREE ------

next0:  OUT     DX,AL                   ; enable a bit in a byte
        MOV     AH,[DI]                 ; latch old data
        MOV     [DI],AH                 ; modify (enabled bits)

        ROR     AL,1                    ; update mask
        ADC     DI,0                    ; update byte address

        TEST    BX,8000H                ; if d >= 0 then ...
        JNZ     dneg0
        ADD     BX,[BP-4]               ; ... d = d + d2
        ADD     DI,[BP-6]               ; update offset to next scan line
        LOOP    next0
        JMP     LineDone

dneg0:  ADD     BX,[BP-2]               ; if d < 0 then d = d + d1
        LOOP    next0
        JMP     LineDone

        ;----------------------------------------------------------------

        ;---    Compute constants for octant one and two

oct12:  MOV     CX,DI                   ; set counter to dy+1
        INC     CX
        SAL     SI,1                    ; d1 = dx * 2
        MOV     BX,SI                   ; d  = dx * 2 - dy
        SUB     BX,DI
        NEG     DI                      ; d2 = -dy + dx * 2 - dy
        ADD     DI,BX

        MOV     [BP-4],DI               ; save d2
        MOV     [BP-2],SI               ; save d1
        MOV     dx,03CEh                ; select BIT MASK register
        MOV     al,08h
        OUT     DX,AL
        INC     DX
        POP     AX                      ; fetch mask
        POP     DI                      ; fetch address
        OUT     DX,AL                   ; enable a bit in a byte

        ;-----GENERATE A LINE IN THE OCTANT ONE AND TWO -----------------

next1:  MOV     AH,[DI]                 ; latch old data
        MOV     [DI],AH                 ; modify (enabled bits)

        ADD     DI,[BP-6]               ; update offset (y = y+1)

        TEST    BX,8000H                ; if d >= 0 then ...
        JNZ     dneg1

        ADD     BX,[BP-4]               ; ... d = d + d2
        ROR     AL,1                    ; ... update mask (x = x+1)
        ADC     DI,0                    ; ... update offset
        OUT     DX,AL                   ; Enable next bit within a byte
        LOOP    next1
        JMP     LineDone

dneg1:  ADD     BX,[BP-2]               ; if d < 0 then d = d + d1
        LOOP    next1

        ;----------------------------------------------------------------
        ;--- Restore PLANE ENABLE and BIT MASK registers
LineDone:
        MOV     DX,03CEh                ; Enable all 8-bits in a byte for write
        MOV     AL,08h                  ; by setting BIT MASK register to Fhex
        OUT     DX,AL
        INC     DX
        MOV     AL,0FFh
        OUT     DX,AL

        DEC     DX                      ; Disable SET/RESET function
        MOV     AL,1
        OUT     DX,AL
        INC     DX
        XOR     AX,AX
        OUT     DX,AL

        POP     ES
        POP     DS
        POP     SI
        POP     DI
        MOV     SP,BP
        POP     BP
        RET
_Line   ENDP
