;	VGAMOIRE.COM

; Structured pattern generator to show off the undocumented
; 320x400 256 color mode of the	IBM VGA as revealed by
; Michael Abrash in his	"On Graphics" column in the
; January/February 1989	issue of Programmer's Journal, Volume 7.1


VGA_SEGMENT		equ	0A000h
SC_INDEX		equ	3C4h
GC_INDEX		equ	3CEh
CRTC_INDEX		equ	3D4h
MAP_MASK		equ	2
MEMORY_MODE		equ	4
MAX_SCAN_LINE		equ	9
START_ADDRESS_HIGH	equ	0Ch
UNDERLINE		equ	14h
MODE_CONTROL		equ	17h
READ_MAP		equ	4
GRAPHICS_MODE		equ	5
MISCELLANEOUS		equ	6
SCREEN_WIDTH		equ	320
SCREEN_HEIGHT		equ	400

tab			equ	9
lf			equ	10
cr			equ	13


CONSTANT_TO_INDEXED_REGISTER	macro	ADDRESS, INDEX, VALUE
	mov	dx, ADDRESS
	mov	ax, (VALUE shl 8) + INDEX
	out	dx, ax
				endm

code	segment
	org	100h
	assume	cs:code, ds:code


Begin:

	push	cs
	pop	ds

	mov	ax, VGA_SEGMENT
	mov	es, ax

	call	Set320x400Mode

	mov	ah, 2Ch
	int	21h
	mov	Random, dx

	mov	si, 256		; ColorCount

	call	Randy
	mov	bp, ax
	call	DoCircles
	
MainLoop:
	call	MakeRandomPalette

	cmp	PageNo, 0
	je	SetPage1
	mov	ax, 0A800h
	mov	es, ax
	CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,0
	mov	PageNo, 0
	jmp	Start
SetPage1:
	mov	ax, 0A000h
	mov	es, ax
	CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,80h
	mov	PageNo, 1

Start:
	call	Randy
	and	ax, 0003h
	cmp	al, 0
	je	Circ
	cmp	al, 1
	je	Sqar
	cmp	al, 2
	je	TiltCirc
	jmp	TiltSqar

Circ:
	call	Randy
	inc	ax
	mov	bp, ax
	call	DoCircles
	jmp	CheckKey

Sqar:
	call	Randy
	inc	ax
	mov	bp, ax
	call	DoSquares
	jmp	CheckKey

TiltCirc:
	call	Randy
	inc	ax
	mov	bp, ax
	call	DoTiltCircles
	jmp	CheckKey

TiltSqar:
	call	Randy
	inc	ax
	mov	bp, ax
	call	DoTiltSquares

CheckKey:
	mov	ah, 0Bh
	int	21h
	cmp	al, 0
	jne	GetOut
	jmp	MainLoop

GetOut:
	mov	ax, 0003h	; Back to Text Mode
	int	10h

	mov	ax, 4C00h
	int	21h

PageNo		dw	1


DoCircles		proc	near

	mov	bl, 00010001b	; Plane Mask bit
	mov	di, 0		; Video memory pointer

	mov	cx, 0		; For Y := 0 to 399 do
ForYCirc:
	push	cx
	mov	cx, 0		; For X := 0 to 319 do
ForXCirc:
	mov	ax, bp		; Get IterateCnt
	mul	cx		; DX:AX := IterateCnt * X
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * X)

	mov	dx, SC_INDEX	; Select the correct plane
	mov	ah, bl
	mov	al, MAP_MASK
	out	dx, ax

	mov	ax, si		; Poke Color
	mov	byte ptr es:[di], al

	rol	bl, 1		; Rotate for next plane
	cmp	bl, 00010001b	; Did we rotate four times?
	jne	DoNextXCirc	; If not then do the next pixel
	inc	di		; Otherwise bump the Video MemPtr

DoNextXCirc:
	inc	cx		; Next X
	cmp	cx, 320
	jl	ForXCirc

	pop	cx

	mov	ax, bp		; Get IterateCnt
	mul	cx		; DX:AX := IterateCnt * Y
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * Y)

	inc	cx		; Next Y
	cmp	cx, 400
	jl	ForYCirc

	ret

DoCircles		endp


DoTiltCircles		proc	near

	mov	bl, 00010001b	; Plane Mask bit
	mov	di, 0		; Video memory pointer

	mov	cx, 0		; For Y := 0 to 399 do
ForYTiltCirc:
	push	cx
	mov	cx, 0		; For X := 0 to 319 do
ForXTiltCirc:
	mov	ax, bp		; Get IterateCnt
	mul	cx		; DX:AX := IterateCnt * X
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * X)

	mov	dx, SC_INDEX	; Select the correct plane
	mov	ah, bl
	mov	al, MAP_MASK
	out	dx, ax

	mov	ax, si		; Poke Color
	mov	byte ptr es:[di], al

	rol	bl, 1		; Rotate for next plane
	cmp	bl, 00010001b	; Did we rotate four times?
	jne	DoNextXTiltCirc	; If not then do the next pixel
	inc	di		; Otherwise bump the Video MemPtr

DoNextXTiltCirc:
	inc	cx		; Next X
	cmp	cx, 320
	jl	ForXTiltCirc

	pop	cx

	mov	ax, bp		; Get IterateCnt
	mul	cx		; DX:AX := IterateCnt * Y
	sub	si, ax		; ColorCount := ColorCount - (IterateCnt * Y)

	inc	cx		; Next Y
	cmp	cx, 400
	jl	ForYTiltCirc

	ret

DoTiltCircles		endp


DoSquares		proc	near

	mov	bl, 00010001b	; Plane Mask bit
	mov	di, 0		; Video memory pointer

	mov	cx, 0		; For Y := 0 to 399 do
ForYSqr:
	mov	CurrY, cx
	push	cx
	mov	cx, 0		; For X := 0 to 319 do
ForXSqr:
	mov	CurrX, cx
	mov	ax, bp		; Get IterateCnt
	mov	dx, CurrY	; Get current Y
	mul	dx		; DX:AX := IterateCnt * Y
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * Y)

	mov	dx, SC_INDEX	; Select the correct plane
	mov	ah, bl
	mov	al, MAP_MASK
	out	dx, ax

	mov	ax, si		; Poke Color
	mov	byte ptr es:[di], al

	rol	bl, 1		; Rotate for next plane
	cmp	bl, 00010001b	; Did we rotate four times?
	jne	DoNextXSqr	; If not then do the next pixel
	inc	di		; Otherwise bump the Video MemPtr

DoNextXSqr:
	inc	cx		; Next X
	cmp	cx, 320
	jl	ForXSqr

	pop	cx

	mov	ax, bp		; Get IterateCnt
	mov	dx, CurrX	; Get current X
	mul	dx		; DX:AX := IterateCnt * X
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * X)

	inc	cx		; Next Y
	cmp	cx, 400
	jl	ForYSqr

	ret

CurrX		dw	(?)
CurrY		dw	(?)

DoSquares		endp


DoTiltSquares		proc	near

	mov	bl, 00010001b	; Plane Mask bit
	mov	di, 0		; Video memory pointer

	mov	cx, 0		; For Y := 0 to 399 do
ForYTiltSqr:
	mov	CurrTiltY, cx
	push	cx
	mov	cx, 0		; For X := 0 to 319 do
ForXTiltSqr:
	mov	CurrTiltX, cx
	mov	ax, bp		; Get IterateCnt
	mov	dx, CurrTiltY	; Get current Y
	mul	dx		; DX:AX := IterateCnt * Y
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * Y)

	mov	dx, SC_INDEX	; Select the correct plane
	mov	ah, bl
	mov	al, MAP_MASK
	out	dx, ax

	mov	ax, si		; Poke Color
	mov	byte ptr es:[di], al

	rol	bl, 1		; Rotate for next plane
	cmp	bl, 00010001b	; Did we rotate four times?
	jne	DoNextXTiltSqr	; If not then do the next pixel
	inc	di		; Otherwise bump the Video MemPtr

DoNextXTiltSqr:
	inc	cx		; Next X
	cmp	cx, 320
	jl	ForXTiltSqr

	pop	cx

	mov	ax, bp		; Get IterateCnt
	mov	dx, CurrTiltX	; Get current X
	mul	dx		; DX:AX := IterateCnt * X
	add	si, ax		; ColorCount := ColorCount + (IterateCnt * X)

	inc	cx		; Next Y
	cmp	cx, 400
	jl	ForYTiltSqr

	ret

CurrTiltX	dw	(?)
CurrTiltY	dw	(?)

DoTiltSquares		endp


Randy			proc	near

;	Returns a pseudo random integer in AX

GetTime:
	mov	ah, 2Ch
	int	21h

	mov	ax, Random
	add	ax, 13
	mul	dx
	add	dx, 23
	mul	dx
	shr	dx, 1
	shr	dx, 1
	shr	dx, 1
	add	ax, dx
	and	ax, 7FFFh
	mov	Random, ax

	ret

Random	dw	(?)

Randy			endp
	

MakeRandomPalette	proc	near

	call	Randy
	mov	byte ptr RED, al
	call	Randy
	mov	byte ptr GREEN, al
	call	Randy
	mov	byte ptr BLUE, al

	mov	cx, 0
NextPal:
	mov	al, cl
	mov	dx, 03C8h	; Set Write PEL Address
	out	dx, al
	inc	dx

	mov	al, byte ptr RED	; Get RED
	and	al, 3Fh
	out	dx, al

	mov	al, byte ptr GREEN	; Get GREEN
	and	al, 3Fh
	out	dx, al

	mov	al, byte ptr BLUE	; Get BLUE
	and	al, 3Fh
	out	dx, al

	inc	byte ptr RED
	inc	byte ptr GREEN
	inc	byte ptr BLUE
	inc	cx
	cmp	cx, 256
	jl	NextPal

	ret

RED	db	(?)
GREEN	db	(?)
BLUE	db	(?)

MakeRandomPalette		endp


Set320x400Mode		proc	near

	mov	ax, 0013h
	int	10h

	mov	dx, SC_INDEX
	mov	al, MEMORY_MODE
	out	dx, al
	inc 	dx
	in	al, dx
	and 	al, NOT 08h
	or	al, 04h
	out	dx, al
	mov 	dx, GC_INDEX
	mov 	al, GRAPHICS_MODE
	out	dx, al
	inc 	dx
	in	al, dx
	and	al, NOT 10h
	out	dx, al
	dec	dx
	mov	al, MISCELLANEOUS
	out	dx, al
	inc	dx
	in	al, dx
	and	al, NOT 02h
	out	dx, al

	CONSTANT_TO_INDEXED_REGISTER  SC_INDEX, MAP_MASK, 0Fh

	mov	ax, VGA_SEGMENT
	mov	es, ax
	sub	di, di
	mov	ax, di
	mov	cx, 8000h
	rep	stosw

	mov	dx, CRTC_INDEX
	mov	al, MAX_SCAN_LINE
	out	dx, al
	inc	dx
	in	al, dx
	and	al, NOT 1Fh
	out	dx, al
	dec	dx

	mov	al, UNDERLINE
	out	dx, al
	inc	dx
	in	al, dx
	and	al, NOT 40h
	out	dx, al
	dec	dx
	mov	al, MODE_CONTROL
	out	dx, al
	inc	dx
	in	al, dx
	or	al, 40h
	out	dx, al

	ret

Set320x400Mode		endp


code	ends

End	Begin
