; Program to illustrate operation of ALUs and latches of the VGA's
;  Graphics Controller.  Draws a variety of patterns against
;  a horizontally striped background, using each of the 4 available
;  logical functions (data unmodified, AND, OR, XOR) in turn to combine
;  the images with the background.
; By Michael Abrash.
;
stack	segment para stack 'STACK'
	db	512 dup(?)
stack	ends
;
VGA_VIDEO_SEGMENT	equ	0a000h	;VGA display memory segment
SCREEN_HEIGHT		equ	350
SCREEN_WIDTH_IN_BYTES	equ	80
DEMO_AREA_HEIGHT	equ	336	;# of scan lines in area
					; logical function operation
					; is demonstrated in
DEMO_AREA_WIDTH_IN_BYTES equ	40	;width in bytes of area
					; logical function operation
					; is demonstrated in
VERTICAL_BOX_WIDTH_IN_BYTES equ 10	;width in bytes of the box used to
					; demonstrate each logical function
;
; VGA register equates.
;
GC_INDEX	equ	3ceh		;GC index register
GC_ROTATE	equ	3		;GC data rotate/logical function
					; register index
GC_MODE		equ	5		;GC mode register index
;
dseg	segment para common 'DATA'
;
; String used to label logical functions.
;
LabelString	label	byte
	db	'UNMODIFIED	 AND	   OR	     XOR   '
LABEL_STRING_LENGTH	equ	$-LabelString
;
; Strings used to label fill patterns.
;
FillPatternFF	db	'Fill Pattern: 0FFh'
FILL_PATTERN_FF_LENGTH	equ	$ - FillPatternFF

FillPattern00	db	'Fill Pattern: 000h'
FILL_PATTERN_00_LENGTH	equ	$ - FillPattern00

FillPatternVert db	'Fill Pattern: Vertical Bar'
FILL_PATTERN_VERT_LENGTH equ	$ - FillPatternVert

FillPatternHorz db	'Fill Pattern: Horizontal Bar'
FILL_PATTERN_HORZ_LENGTH equ	$ - FillPatternHorz
;
dseg	ends
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC	macro	INDEX, SETTING
	mov	dx,GC_INDEX
	mov	ax,(SETTING SHL 8) OR INDEX
	out	dx,ax
	endm
;
;
; Macro to call BIOS write string function to display text string
;  TEXT_STRING, of length TEXT_LENGTH, at location ROW,COLUMN.
;
TEXT_UP macro	TEXT_STRING, TEXT_LENGTH, ROW, COLUMN
	mov	ah,13h			;BIOS write string function
	mov	bp,offset TEXT_STRING	;ES:BP points to string
	mov	cx,TEXT_LENGTH
	mov	dx,(ROW SHL 8) OR COLUMN;position
	sub	al,al			;string is chars only, cursor not moved
	mov	bl,7			;text attribute is white (light gray)
	int	10h
	endm
;
cseg	segment para public 'CODE'
	assume	cs:cseg, ds:dseg
start	proc	near
	mov	ax,dseg
	mov	ds,ax
;
; Select 640x350 graphics mode.
;
	mov	ax,010h
	int	10h
;
; ES points to VGA memory.
;
	mov	ax,VGA_VIDEO_SEGMENT
	mov	es,ax
;
; Draw background of horizontal bars.
;
	mov	dx,SCREEN_HEIGHT/4
					;# of bars to draw (each 4 pixels high)
	sub	di,di			;start at offset 0 in display memory
	mov	ax,0ffffh		;fill pattern for light areas of bars
	mov	bx,DEMO_AREA_WIDTH_IN_BYTES / 2 ;length of each bar
	mov	si,SCREEN_WIDTH_IN_BYTES - DEMO_AREA_WIDTH_IN_BYTES
	mov	bp,(SCREEN_WIDTH_IN_BYTES * 3) - DEMO_AREA_WIDTH_IN_BYTES
BackgroundLoop:
	mov	cx,bx			;length of bar
	rep stosw			;draw top half of bar
	add	di,si			;point to start of bottom half of bar
	mov	cx,bx			;length of bar
	rep stosw			;draw bottom half of bar
	add	di,bp			;point to start of top of next bar
	dec	dx
	jnz	BackgroundLoop
;
; Draw vertical boxes filled with a variety of fill patterns
;  using each of the 4 logical functions in turn.
;
	SETGC	GC_ROTATE, 0		;select data unmodified
					; logical function...
	mov	di,0
	call	DrawVerticalBox		;...and draw box
;
	SETGC	GC_ROTATE, 08h		;select AND logical function...
	mov	di,10
	call	DrawVerticalBox		;...and draw box
;
	SETGC	GC_ROTATE, 10h		;select OR logical function...
	mov	di,20
	call	DrawVerticalBox		;...and draw box
;
	SETGC	GC_ROTATE, 18h		;select XOR logical function...
	mov	di,30
	call	DrawVerticalBox		;...and draw box
;
; Reset the logical function to data unmodified, the default state.
;
	SETGC	GC_ROTATE, 0
;
; Label the screen.
;
	push	ds
	pop	es	;strings we'll display are passed to BIOS
			; by pointing ES:BP to them
;
; Label the logical functions, using the VGA BIOS's
;  write string function.
;
	TEXT_UP LabelString, LABEL_STRING_LENGTH, 24, 0
;
; Label the fill patterns, using the VGA BIOS's
;  write string function.
;
	TEXT_UP FillPatternFF, FILL_PATTERN_FF_LENGTH, 3, 42
	TEXT_UP FillPattern00, FILL_PATTERN_00_LENGTH, 9, 42
	TEXT_UP FillPatternVert, FILL_PATTERN_VERT_LENGTH, 15, 42
	TEXT_UP FillPatternHorz, FILL_PATTERN_HORZ_LENGTH, 21, 42
;
; Wait until a key's been hit to reset screen mode & exit.
;
WaitForKey:
	mov	ah,1
	int	16h
	jz	WaitForKey
;
; Finished.  Clear key, reset screen mode and exit.
;
Done:
	mov	ah,0	;clear key that we just detected
	int	16h
;
	mov	ax,3	;reset to text mode
	int	10h
;
	mov	ah,4ch	;exit to DOS
	int	21h
;
start	endp
;
; Subroutine to draw a box 80x336 in size, using currently selected
;  logical function, with upper left corner at the display memory offset
;  in DI.  Box is filled with four patterns.  Top quarter of area is
;  filled with 0FFh (solid) pattern, next quarter is filled with 00h
;  (empty) pattern, next quarter is filled with 33h (double pixel wide
;  vertical bar) pattern, and bottom quarter is filled with double pixel
;  high horizontal bar pattern.
;
; Macro to draw a column of the specified width in bytes, one-quarter
;  of the height of the box, with the specified fill pattern.
;
DRAW_BOX_QUARTER	macro	FILL, WIDTH
	local	RowLoop, ColumnLoop
	mov	al,FILL			;fill pattern
	mov	dx,DEMO_AREA_HEIGHT / 4 ;1/4 of the full box height
RowLoop:
	mov	cx,WIDTH
ColumnLoop:
	mov	ah,es:[di]	;load display memory contents into
				; GC latches (we don't actually care
				; about value read into AH)
	stosb			;write pattern, which is logically
				; combined with latch contents for each
				; plane and then written to display
				; memory
	loop	ColumnLoop
	add	di,SCREEN_WIDTH_IN_BYTES - WIDTH
				;point to start of next line down in box
	dec	dx
	jnz	RowLoop
	endm
;
DrawVerticalBox proc	near
	DRAW_BOX_QUARTER	0ffh, VERTICAL_BOX_WIDTH_IN_BYTES
				;first fill pattern: solid fill
	DRAW_BOX_QUARTER	0, VERTICAL_BOX_WIDTH_IN_BYTES
				;second fill pattern: empty fill
	DRAW_BOX_QUARTER	033h, VERTICAL_BOX_WIDTH_IN_BYTES
				;third fill pattern: double-pixel
				; wide vertical bars
	mov	dx,DEMO_AREA_HEIGHT / 4 / 4
				;fourth fill pattern: horizontal bars in
				; sets of 4 scan lines
	sub	ax,ax
	mov	si,VERTICAL_BOX_WIDTH_IN_BYTES	;width of fill area
HorzBarLoop:
	dec	ax		;0ffh fill (smaller to do word than byte DEC)
	mov	cx,si		;width to fill
HBLoop1:
	mov	bl,es:[di]	;load latches (don't care about value)
	stosb			;write solid pattern, through ALUs
	loop	HBLoop1
	add	di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
	mov	cx,si		;width to fill
HBLoop2:
	mov	bl,es:[di]	;load latches
	stosb			;write solid pattern, through ALUs
	loop	HBLoop2
	add	di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
	inc	ax		;0 fill (smaller to do word than byte DEC)
	mov	cx,si		;width to fill
HBLoop3:
	mov	bl,es:[di]	;load latches
	stosb			;write empty pattern, through ALUs
	loop	HBLoop3
	add	di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
	mov	cx,si		;width to fill
HBLoop4:
	mov	bl,es:[di]	;load latches
	stosb			;write empty pattern, through ALUs
	loop	HBLoop4
	add	di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
	dec	dx
	jnz	HorzBarLoop
;
	ret
DrawVerticalBox endp
cseg	ends
	end	start
