	TITLE	VGALINE - Fast line drawing routine.
	NAME	VGALINE

	COMMENT	$

	Name:		VGALINE

		Written and (c) by Dave Stampe 9/11/91
		Not for commercial use, so get permission
		before marketing code using this stuff!
		For private PD use only.

		$



	COMMENT	$

	Name:		VGALINE

	Function:	Draw a line in VGA 200 line 16 colour mode

	Caller:		C:

			void vgaline(x1, y1, x2, y2, n);
				int	x1, y1, x2, y2;	/* pixel co-ords */
				int	n;		/* color */

			no checking on endpoints!

	Adapted from Richard Wilton's code (PC and PS/2 Video Systems).
	Modified to use VGA write mode 3 (much faster)
	Call setup_hdwe()  before drawing groups of lines

		$



		.MODEL large
		.CODE

BytesPerLine	EQU		40
		extrn  _dpaddr

egapaddr	PROC	far

		mov		cl,bl
		push		dx

		mov		dx,BytesPerLine
		mul		dx

		pop		dx
		shr		bx,1
		shr		bx,1
		shr		bx,1
		add		bx,ax
		add		bx,word ptr ds:_dpaddr

		mov		ax,0a000h
		mov		es,ax

		and		cl,7
		xor		cl,7
		mov		ah,1
		ret

egapaddr	endp



; Stack frame addressing - LARGE CODE MODEL

ARGx1		EQU	word ptr [bp+6]
ARGy1		EQU	word ptr [bp+8]
ARGx2		EQU	word ptr [bp+10]
ARGy2		EQU	word ptr [bp+12]
ARGn		EQU	byte ptr [bp+14]

VARvertincr	EQU	word ptr [bp-6]
VARincr1	EQU	word ptr [bp-8]
VARincr2	EQU	word ptr [bp-10]
VARroutine	EQU	word ptr [bp-12]

ByteOffsetShift	EQU	3
RMWbits		EQU	0

		PUBLIC	_vgaline

_vgaline	PROC	far

		push	bp		; Set up stack frame
		mov	bp,sp
		sub	sp,14
		push	si
		push	di

; set color

	mov	dx,03CEH
	xor	al,al
	mov	ah,ARGn
	out	dx,ax

; check for vertical line

		mov	si,BytesPerLine
		mov	cx,ARGx2
		sub	cx,ARGx1
		jz	VertLine

; force x1 < x2

		jns	L01

		neg	cx

		mov	bx,ARGx2
		xchg	bx,ARGx1
		mov	ARGx2,bx

		mov	bx,ARGy2
		xchg	bx,ARGy1
		mov	ARGy2,bx

; calc dy = abs(y2 - y1)

L01:
		mov	bx,ARGy2
		sub	bx,ARGy1
		jz	HorizLine
		jns	L03

		neg	bx
		neg	si

; select appropriate routine for slope of line

L03:
		mov	VARvertincr,si
		mov	VARroutine,offset LoSlopeLine
		cmp	bx,cx
		jle	L04
		mov	VARroutine,offset HiSlopeLine
		xchg	bx,cx

; calc initial decision variable and increments

L04:
		shl	bx,1
		mov	VARincr1,bx
		sub	bx,cx
		mov	si,bx
		sub	bx,cx
		mov	VARincr2,bx

; calc first pixel address

		push	cx
		mov	ax,ARGy1
		mov	bx,ARGx1
		call	egapaddr
		mov	di,bx
		shl	ah,cl
		mov	bl,ah
		pop	cx
		inc	cx
		jmp	VARroutine

; routine for verticle lines

VertLine:
		mov	ax,ARGy1
		mov	bx,ARGy2
		mov	cx,bx
		sub	cx,ax
		jge	L31
		neg	cx
		mov	ax,bx

L31:
		inc	cx
		mov	bx,ARGx1
		push	cx
		call	egapaddr

		shl	ah,cl
		pop	cx

; draw the line

L32:
		and	es:[bx],ah
		add	bx,si
		loop	L32
		jmp	Lexit

; routine for horizontal line

HorizLine:
		push	ds

		mov	ax,ARGy1
		mov	bx,ARGx1
		call	egapaddr
		mov	di,bx
		mov	dh,ah
		not	dh
		shl	dh,cl
		not	dh

		mov	cx,ARGx2
		and	cl,7
		xor	cl,7
		mov	dl,0FFH
		shl	dl,cl

; determine byte offset of first and last pixel in line

		mov	ax,ARGx2
		mov	bx,ARGx1
		mov	cl,ByteOffsetShift

		shr	ax,cl
		shr	bx,cl
		mov	cx,ax
		sub	cx,bx

		mov	bx,dx

; set pixels in leftmost byte of line

		or	bh,bh
		js	L43
		or	cx,cx
		jnz	L42
		and	bl,bh
		jmp	short L44

L42:
		and	es:[di],bh
		inc	di
		dec	cx

; draw remainder of the line

L43:
		mov	al,0FFH
		rep	stosb

; set pixels in rightmost byte of line

L44:
		and     es:[di],bl
		pop	ds
		jmp	short Lexit



; routine for dy >= dx (slope <= 1)

LoSlopeLine:

L10:
		mov	ah,bl

L11:
		or	ah,bl
		ror	bl,1
		jc	L14

; bit mask not shifted out

		or	si,si
		jns	L12
		add	si,VARincr1
		loop	L11

		and	es:[di],ah
		jmp	short Lexit

L12:
		add	si,VARincr2
		and	es:[di],ah
		add	di,VARvertincr
		loop	L10
		jmp	short Lexit

; bit mask shifted out

L14:
		and	es:[di],ah
		inc	di
		or	si,si
		jns	L15
		add	si,VARincr1
		loop	L10
		jmp	short Lexit

L15:
		add	si,VARincr2
		add	di,VARvertincr
		loop	L10
		jmp	short Lexit



; routine for dy > dx (slope > 1)

HiSlopeLine:
		mov	bx,VARvertincr

L21:
		and	es:[di],ah
		add	di,bx

L22:
		or	si,si
		jns	L23

		add	si,VARincr1
		loop	L21
		jmp	short Lexit

L23:
		add	si,VARincr2
		ror	ah,1
		adc	di,0
		loop	L21

; return to caller

Lexit:
		pop	di
		pop	si
		mov	sp,bp
		pop	bp
		ret

_vgaline	endp




   ;
   ;	int clipper (lpoints far *lp)
   ;
   ;  /* returns 0 if unclipped, 1 if clipped, and -1 if invisible */
   ;
   ;  Improved assembly version of Sutherland-Cohen line clipper
   ;  Much more optimized than C version, and knows when to
   ;  stop clipping!
   ;

	PUBLIC	_clipper

x1	EQU	word ptr es:[bx]     ; elements in point array
y1	EQU	word ptr es:[bx+2]
x2	EQU	word ptr es:[bx+4]
y2	EQU	word ptr es:[bx+6]

left  equ 8                          ; clipping flag bits
above equ 4
right equ 2
below equ 1

	EXTRN	_l_clip              ; clipping recangle (assumed in ds)
	EXTRN	_r_clip
	EXTRN	_t_clip
	EXTRN	_b_clip


_clipper	proc	far

	.386				; required for jump length only

	push	bp
	mov	bp,sp
	dec	sp
	dec	sp
	push	si
	push	di
	les	bx,dword ptr [bp+6]     ; setup access to points

	xor	di,di			; flag1 = 0

	mov	ax,x1
	cmp	ax,word ptr ds:_l_clip  ; set flag bits based on pos'n
	jge	short nleft1            ; for point 1
	or	di,8
nleft1:
	cmp	ax,word ptr ds:_r_clip
	jle	short nright1
	or	di,2
nright1:
	mov	ax,y1
	cmp	ax,word ptr ds:_t_clip
	jge	short ntop1
	or	di,4
ntop1:
	cmp	ax,word ptr ds:_b_clip
	jle	short nbot1
	or	di,1
nbot1:
	xor	si,si			; flag2 = 0

	mov	ax,x2
	cmp	ax,word ptr ds:_l_clip  ; set flag bits based on pos'n
	jge	short nleft2            ; for point 2
	or	si,8
nleft2:
	cmp	ax,word ptr ds:_r_clip
	jle	short nright2
	or	si,2
nright2:
	mov	ax,y2
	cmp	ax,word ptr ds:_t_clip
	jge	short ntop2
	or	si,4
ntop2:
	cmp	ax,word ptr ds:_b_clip
	jle	short nbot2
	or	si,1
nbot2:

	mov	ax,di			; check if all inside rect
	or	ax,si
	jne	short not_in_box
	xor	ax,ax                   ; return 0: not clipped
rexit:
	pop	di
	pop	si                      ; early return
	mov	sp,bp
	pop	bp
	ret

not_in_box:
	test	di,si                   ; check if all out of rect
	je	short needs_clipping
diagerr:                                ; too many clipping (diagonal)
	mov	ax,65535
	jmp	rexit                   ; return -1 (out of window)

needs_clipping:                         ; gotta do some serious work...

	or	di,di			; quick test if pt.1 OK
	jne	clip1
	jmp	do2

clip1:   				; clipping point 1:

	test	di,8                    ; left flag?
	je	short notleft1

doleft1:
   ;
   ;	    lp->y1 += (long)(lp->y2-lp->y1)*(l_clip-lp->x1)/(lp->x2-lp->x1);
   ;
	mov	cx,x2                   ; denominator: 0?
	sub	cx,x1
	je	dontleft1

	mov	ax,y2
	sub	ax,y1
	mov	dx,ds:_l_clip
	sub	dx,x1
	imul	dx
	idiv	cx
	add	y1,ax

dontleft1:
	mov	dx,ds:_l_clip           ; clip left
	mov	x1,dx

	mov	ax,y1                   ; check if vert. clipping needed
	cmp	ax,word ptr ds:_t_clip
	jl	doabove1
	cmp	ax,word ptr ds:_b_clip
	jg	dobelow1
	jmp	do2                     ; else check point 2

notleft1:
	test	di,2			; test if right needs clipping
	je	notright1

doright1:
   ;
   ;	   lp->y1 += (long)(lp->y2-lp->y1)*(r_clip-lp->x1)/(lp->x2-lp->x1);
   ;
	mov	cx,x2                   ; denominator: 0?
	sub	cx,x1
	je	dontright1

	mov	ax,y2
	sub	ax,y1
	mov	dx,ds:_r_clip
	sub	dx,x1
	imul	dx
	idiv	cx
	add	y1,ax

dontright1:
	mov	dx,ds:_r_clip           ; clip right
	mov	x1,dx

	mov	ax,y1
	cmp	ax,word ptr ds:_t_clip  ; check if vert. clipping needed
	jl	doabove1
	cmp	ax,word ptr ds:_b_clip
	jg	dobelow1
	jmp	do2

notright1:
	test	di,4			; test if top clip needed
	je	short notabove1

doabove1:
   ;
   ;	 lp->x1 += (long)(lp->x2-lp->x1)*(t_clip-lp->y1)/(lp->y2-lp->y1);
   ;
	mov	cx,y2                   ; denominator: 0?
	sub	cx,y1
	je	dontabove1

	mov	ax,x2
	sub	ax,x1
	mov	dx,ds:_t_clip
	sub	dx,y1
	imul	dx
	idiv	cx
	add	x1,ax

dontabove1:
	mov	dx,ds:_t_clip           ; clip top
	mov	y1,dx

	mov	ax,x1
	cmp	ax,word ptr ds:_l_clip  ; if hor. clip req, diagonal outside
	jl	diagerr ; doleft1
	cmp	ax,word ptr ds:_r_clip
	jg	diagerr ;doright1
	jmp	short do2

notabove1:
	test	di,1			; test if bottom needs clipping
	je	short do2

dobelow1:
   ;
   ;	   lp->x1 += (long)(lp->x2-lp->x1)*(b_clip-lp->y1)/(lp->y2-lp->y1);
   ;
	mov	cx,y2                   ; denominator: 0?
	sub	cx,y1
	je	dontbelow1

	mov	ax,x2
	sub	ax,x1
	mov	dx,ds:_b_clip
	sub	dx,y1
	imul	dx
	idiv	cx
	add	x1,ax

dontbelow1:
	mov	dx,ds:_b_clip
	mov	y1,dx

	mov	ax,x1
	cmp	ax,ds:_l_clip
	jl	diagerr ;oleft1
	cmp	ax,word ptr ds:_r_clip
	jg	diagerr ;doright1




do2:
	or	si,si		; same deal for point 2
	je	done2
	test	si,8
	je	short notleft2

doleft2:
   ;
   ;      lp->y2 += (long)(lp->y1-lp->y2)*(l_clip-lp->x2)/(lp->x1-lp->x2);
   ;
	mov	cx,x1                   ; denominator: 0?
	sub	cx,x2
	je	dontleft2

	mov	ax,y1
	sub	ax,y2
	mov	dx,ds:_l_clip
	sub	dx,x2
	imul	dx
	idiv	cx
	add	y2,ax

dontleft2:
	mov	dx,ds:_l_clip
	mov	x2,dx

	mov	ax,y2
	cmp	ax,word ptr ds:_t_clip
	jl	doabove2
	cmp	ax,word ptr ds:_b_clip
	jg	dobelow2
	jmp	done2

notleft2:
	test	si,2			; test if right needs clipping
	je	notright2

doright2:
   ;
   ;      lp->y2 += (long)(lp->y1-lp->y2)*(r_clip-lp->x2)/(lp->x1-lp->x2);
   ;
	mov	cx,x1                   ; denominator: 0?
	sub	cx,x2
	je	dontright2

	mov	ax,y1
	sub	ax,y2
	mov	dx,ds:_r_clip
	sub	dx,x2
	imul	dx
	idiv	cx
	add	y2,ax

dontright2:
	mov	dx,ds:_r_clip
	mov	x2,dx

	mov	ax,y2
	cmp	ax,word ptr ds:_t_clip
	jl	doabove2
	cmp	ax,word ptr ds:_b_clip
	jg	dobelow2
	jmp	done2

notright2:
	test	si,4			; test if top clip needed
	je	short notabove2

doabove2:
   ;
   ;      lp->x2 += (long)(lp->x1-lp->x2)*(t_clip-lp->y2)/(lp->y1-lp->y2);
   ;
	mov	cx,y1                   ; denominator: 0?
	sub	cx,y2
	je	dontabove2

	mov	ax,x1
	sub	ax,x2
	mov	dx,ds:_t_clip
	sub	dx,y2
	imul	dx
	idiv	cx
	add	x2,ax

dontabove2:
	mov	dx,ds:_t_clip
	mov	y2,dx

	mov	ax,x2
	cmp	ax,word ptr ds:_l_clip
	jl	diagerr ;doleft2
	cmp	ax,word ptr ds:_r_clip
	jg	diagerr ;doright2
	jmp	short done2

notabove2:
	test	si,1			; test if bottom needs clipping
	je	short done2

dobelow2:
   ;
   ;	  lp->x2 += (long)(lp->x1-lp->x2)*(b_clip-lp->y2)/(lp->y1-lp->y2);
   ;
	mov	cx,y1                   ; denominator: 0?
	sub	cx,y2
	je	dontbelow2

	mov	ax,x1
	sub	ax,x2
	mov	dx,ds:_b_clip
	sub	dx,y2
	imul	dx
	idiv	cx
	add	x2,ax

dontbelow2:
	mov	dx,ds:_b_clip
	mov	y2,dx

	mov	ax,x2
	cmp	ax,ds:_l_clip
	jl	diagerr ;doleft2
	cmp	ax,word ptr ds:_r_clip
	jg	diagerr ;doright2

done2:			; finished point 2
	mov	ax,1    ; return 1 for successful clipping
	pop	di
	pop	si
	mov	sp,bp
	pop	bp
	ret

_clipper	endp


	end