; Program to illustrate one use of write mode 2 of the VGA and EGA by
; drawing lines in color patterns.
;
; Assemble with MASM or TASM
;
; By Michael Abrash
;
Stack	segment para stack 'STACK'
	db	512 dup(0)
Stack	ends

SCREEN_WIDTH_IN_BYTES	equ	80
GRAPHICS_SEGMENT	equ	0a000h	;mode 10 bit-map segment

SC_INDEX	equ	3c4h	;Sequence Controller Index register
MAP_MASK	equ	2	;index of Map Mask register
GC_INDEX	equ	03ceh	;Graphics Controller Index reg
GRAPHICS_MODE	equ	5	;index of Graphics Mode reg
BIT_MASK	equ	8	;index of Bit Mask reg

Data	segment para common 'DATA'
Pattern0	db	16
		db	0, 1, 2, 3, 4, 5, 6, 7, 8
		db	9, 10, 11, 12, 13, 14, 15
Pattern1	db	6
		db	2, 2, 2, 10, 10, 10
Pattern2	db	8
		db	15, 15, 15, 0, 0, 15, 0, 0
Pattern3	db	9
		db	1, 1, 1, 2, 2, 2, 4, 4, 4
Data	ends

Code	segment para public 'CODE'
	assume	cs:Code, ds:Data
Start	proc	near
	mov	ax,Data
	mov	ds,ax
	mov	ax,10h
	int	10h		;select video mode 10h (640x350)
;
; Draw 8 radial lines in upper-left quadrant in pattern 0.
;
	mov	bx,0
	mov	cx,0
	mov	si,offset Pattern0
	call	QuadrantUp
;
; Draw 8 radial lines in upper-right quadrant in pattern 1.
;
	mov	bx,320
	mov	cx,0
	mov	si,offset Pattern1
	call	QuadrantUp
;
; Draw 8 radial lines in lower-left quadrant in pattern 2.
;
	mov	bx,0
	mov	cx,175
	mov	si,offset Pattern2
	call	QuadrantUp
;
; Draw 8 radial lines in lower-right quadrant in pattern 3.
;
	mov	bx,320
	mov	cx,175
	mov	si,offset Pattern3
	call	QuadrantUp
;
; Wait for a key before returning to text mode and ending.
;
	mov	ah,01h
	int	21h
	mov	ax,03h
	int	10h
	mov	ah,4ch
	int	21h
;
; Draws 8 radial lines with specified pattern in specified mode 10h
; quadrant.
;
; Input:
;	BX = X coordinate of upper left corner of quadrant
;	CX = Y coordinate of upper left corner of quadrant
;	SI = pointer to pattern, in following form:
;		Byte 0: Length of pattern
;		Byte 1: Start of pattern, one color per byte
;
; AX, BX, CX, DX destroyed
;
QuadrantUp	proc	near
	add	bx,160
	add	cx,87		;point to the center of the quadrant
	mov	ax,0
	mov	dx,160
	call	LineUp		;draw horizontal line to right edge
	mov	ax,1
	mov	dx,88
	call	LineUp		;draw diagonal line to upper right
	mov	ax,2
	mov	dx,88
	call	LineUp		;draw vertical line to top edge
	mov	ax,3
	mov	dx,88
	call	LineUp		;draw diagonal line to upper left
	mov	ax,4
	mov	dx,161
	call	LineUp		;draw horizontal line to left edge
	mov	ax,5
	mov	dx,88
	call	LineUp		;draw diagonal line to lower left
	mov	ax,6
	mov	dx,88
	call	LineUp		;draw vertical line to bottom edge
	mov	ax,7
	mov	dx,88
	call	LineUp		;draw diagonal line to bottom right
	ret
QuadrantUp	endp
;
; Draws a horizontal, vertical, or diagonal line (one of the eight
; possible radial lines) of the specified length from the specified
; starting point.
;
; Input:
;	AX = line direction, as follows:
;		3  2  1
;		4  *  0
;		5  6  7
;	BX = X coordinate of starting point
;	CX = Y coordinate of starting point
;	DX = length of line (number of pixels drawn)
;
; All registers preserved.
;
; Table of vectors to routines for each of the 8 possible lines.
;
LineUpVectors	label	word
	dw	LineUp0, LineUp1, LineUp2, LineUp3
	dw	LineUp4, LineUp5, LineUp6, LineUp7

;
; Macro to draw horizontal, vertical, or diagonal line.
;
; Input:
;	XParm = 1 to draw right, -1 to draw left, 0 to not move horz.
;	YParm = 1 to draw up, -1 to draw down, 0 to not move vert.
;	BX = X start location
;	CX = Y start location
;	DX = number of pixels to draw
;	DS:SI = line pattern
;
MLineUp macro	XParm, YParm
	local	LineUpLoop, CheckMoreLine
	mov	di,si		;set aside start offset of pattern
	lodsb			;get length of pattern
	mov	ah,al

LineUpLoop:
	lodsb			;get color of this pixel...
	call	DotUpInColor	;...and draw it
if XParm EQ 1
	inc	bx
endif
if XParm EQ -1
	dec	bx
endif
if YParm EQ 1
	inc	cx
endif
if YParm EQ -1
	dec	cx
endif
	dec	ah		;at end of pattern?
	jnz	CheckMoreLine
	mov	si,di		;get back start of pattern
	lodsb
	mov	ah,al		;reset pattern count

CheckMoreLine:
	dec	dx
	jnz	LineUpLoop
	jmp	LineUpEnd
	endm

LineUp	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	es

	mov	di,ax

	mov	ax,GRAPHICS_SEGMENT
	mov	es,ax

	push	dx		;save line length
;
; Enable writes to all planes.
;
	mov	dx,SC_INDEX
	mov	al,MAP_MASK
	out	dx,al
	inc	dx
	mov	al,0fh
	out	dx,al
;
; Select write mode 2.
;
	mov	dx,GC_INDEX
	mov	al,GRAPHICS_MODE
	out	dx,al
	inc	dx
	mov	al,02h
	out	dx,al
;
; Vector to proper routine.
;
	pop	dx		;get back line length

	shl	di,1
	jmp	cs:[LineUpVectors+di]
;
; Horizontal line to right.
;
LineUp0:
	MLineUp 1, 0
;
; Diagonal line to upper right.
;
LineUp1:
	MLineUp 1, -1
;
; Vertical line to top.
;
LineUp2:
	MLineUp 0, -1
;
; Diagonal line to upper left.
;
LineUp3:
	MLineUp -1, -1
;
; Horizontal line to left.
;
LineUp4:
	MLineUp -1, 0
;
; Diagonal line to bottom left.
;
LineUp5:
	MLineUp -1, 1
;
; Vertical line to bottom.
;
LineUp6:
	MLineUp 0, 1
;
; Diagonal line to bottom right.
;
LineUp7:
	MLineUp 1, 1

LineUpEnd:
	pop	es
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
LineUp	endp
;
; Draws a dot in the specified color at the specified location.
; Assumes that the VGA is in write mode 2 with writes to all planes
; enabled and that ES points to display memory.
;
; Input:
;	AL = dot color
;	BX = X coordinate of dot
;	CX = Y coordinate of dot
;	ES = display memory segment
;
; All registers preserved.
;
DotUpInColor	proc	near
	push	bx
	push	cx
	push	dx
	push	di
;
; Point ES:DI to the display memory byte in which the pixel goes, with
; the bit mask set up to access that pixel within the addressed byte.
;
	push	ax		;preserve dot color
	mov	ax,SCREEN_WIDTH_IN_BYTES
	mul	cx		;offset of start of top scan line
	mov	di,ax
	mov	cl,bl
	and	cl,111b
	mov	dx,GC_INDEX
	mov	al,BIT_MASK
	out	dx,al
	inc	dx
	mov	al,80h
	shr	al,cl
	out	dx,al		;set the bit mask for the pixel
	shr	bx,1
	shr	bx,1
	shr	bx,1		;X in bytes
	add	di,bx		;offset of byte pixel is in
	mov	al,es:[di]	;load latches
	pop	ax		;get back dot color
	stosb			;write dot in desired color

	pop	di
	pop	dx
	pop	cx
	pop	bx
	ret
DotUpInColor	endp
Start	endp
Code	ends
	end	Start
