; Program to demonstrate pixel drawing in 320x400 256-color
; mode on the VGA. Draws 8 lines to form an octagon, a pixel
; at a time. Draws 8 octagons in all, one on top of the other,
; each in a different color set. Although it's not used, a
; pixel read function is also provided.
;
VGA_SEGMENT	equ	0a000h
SC_INDEX	equ	3c4h	;Sequence Controller Index register
GC_INDEX	equ	3ceh	;Graphics Controller Index register
CRTC_INDEX	equ	3d4h	;CRT Controller Index register
MAP_MASK	equ	2	;Map Mask register index in SC
MEMORY_MODE	equ	4	;Memory Mode register index in SC
MAX_SCAN_LINE	equ	9	;Maximum Scan Line reg index in CRTC
START_ADDRESS_HIGH equ	0ch	;Start Address High reg index in CRTC
UNDERLINE	equ	14h	;Underline Location reg index in CRTC
MODE_CONTROL	equ	17h	;Mode Control register index in CRTC
READ_MAP	equ	4	;Read Map register index in GC
GRAPHICS_MODE	equ	5	;Graphics Mode register index in GC
MISCELLANEOUS	equ	6	;Miscellaneous register index in GC
SCREEN_WIDTH	equ	320	;# of pixels across screen
SCREEN_HEIGHT	equ	400	;# of scan lines on screen
WORD_OUTS_OK	equ	1	;set to 0 to assemble for
				; computers that can't handle
				; word outs to indexed VGA registers
;
stack	segment para stack 'STACK'
	db	512 dup (?)
stack	ends
;
Data	segment word 'DATA'
;
BaseColor	db	0
;
; Structure used to control drawing of a line.
;
LineControl	struc
StartX		dw	?
StartY		dw	?
LineXInc	dw	?
LineYInc	dw	?
BaseLength	dw	?
LineColor	db	?
LineControl	ends
;
; List of descriptors for lines to draw.
;
LineList	label	LineControl
	LineControl	<130,110,1,0,60,0>
	LineControl	<190,110,1,1,60,1>
	LineControl	<250,170,0,1,60,2>
	LineControl	<250,230,-1,1,60,3>
	LineControl	<190,290,-1,0,60,4>
	LineControl	<130,290,-1,-1,60,5>
	LineControl	<70,230,0,-1,60,6>
	LineControl	<70,170,1,-1,60,7>
	LineControl	<-1,0,0,0,0,0>
Data	ends
;
; Macro to output a word value to a port.
;
OUT_WORD	macro
if WORD_OUTS_OK
	out	dx,ax
else
	out	dx,al
	inc	dx
	xchg	ah,al
	out	dx,al
	dec	dx
	xchg	ah,al
endif
	endm
;
; Macro to output a constant value to an indexed VGA register.
;
CONSTANT_TO_INDEXED_REGISTER	macro	ADDRESS, INDEX, VALUE
	mov	dx,ADDRESS
	mov	ax,(VALUE shl 8) + INDEX
	OUT_WORD
	endm
;
Code	segment
	assume	cs:Code, ds:Data
Start	proc	near
	mov	ax,Data
	mov	ds,ax
;
; Set 320x400 256-color mode.
;
	call	Set320By400Mode
;
; We're in 320x400 256-color mode. Draw each line in turn.
;
ColorLoop:
	mov	si,offset LineList ;point to the start of the
				; line descriptor list
LineLoop:
	mov	cx,[si+StartX]	;set the initial X coordinate
	cmp	cx,-1
	jz	LinesDone	;a descriptor with a -1 X
				; coordinate marks the end
				; of the list
	mov	dx,[si+StartY]	;set the initial Y coordinate,
	mov	bl,[si+LineColor]	; line color,
	mov	bp,[si+BaseLength]	; and pixel count
	add	bl,[BaseColor]	;adjust the line color according
				; to BaseColor
PixelLoop:
	push	cx		;save the coordinates
	push	dx
	call	WritePixel	;draw this pixel
	pop	dx		;retrieve the coordinates
	pop	cx
	add	cx,[si+LineXInc];set the coordinates of the
	add	dx,[si+LineYInc]; next point of the line
	dec	bp		;any more points?
	jnz	PixelLoop	;yes, draw the next
	add	si,size LineControl	;point to the next line descriptor
	jmp	LineLoop	; and draw the next line
LinesDone:
	call	GetNextKey	;wait for a key, then
	inc	[BaseColor]	; bump the color selection and
	cmp	[BaseColor],8	; see if we're done
	jb	ColorLoop	;not done yet
;
; Wait for a key and return to text mode and end when
; one is pressed.
;
	call	GetNextKey
	mov	ax,0003h
	int	10h	;text mode
	mov	ah,4ch
	int	21h	;done
;
Start	endp
;
; Sets up 320x400 256-color modes.
;
; Input: none
;
; Output: none
;
Set320By400Mode proc	near
;
; First, go to normal 320x200 256-color mode, which is really a
; 320x400 256-color mode with each line scanned twice.
;
	mov	ax,0013h	;AH = 0 means mode set, AL = 13h selects
				; 256-color graphics mode
	int	10h		;BIOS video interrupt
;
; Change CPU addressing of video memory to linear (not odd/even,
; chain, or chain 4), to allow us to access all 256K of display
; memory. When this is done, VGA memory will look just like memory
; in modes 10h and 12h, except that each byte of display memory will
; control one 256-color pixel, with 4 adjacent pixels at any given
; address, one pixel per plane.
;
	mov	dx,SC_INDEX
	mov	al,MEMORY_MODE
	out	dx,al
	inc	dx
	in	al,dx
	and	al,not 08h	;turn off chain 4
	or	al,04h		;turn off odd/even
	out	dx,al
	mov	dx,GC_INDEX
	mov	al,GRAPHICS_MODE
	out	dx,al
	inc	dx
	in	al,dx
	and	al,not 10h	;turn off odd/even
	out	dx,al
	dec	dx
	mov	al,MISCELLANEOUS
	out	dx,al
	inc	dx
	in	al,dx
	and	al,not 02h	;turn off chain
	out	dx,al
;
; Now clear the whole screen, since the mode 13h mode set only
; cleared 64K out of the 256K of display memory. Do this before
; we switch the CRTC out of mode 13h, so we don't see garbage
; on the screen when we make the switch.
;
	CONSTANT_TO_INDEXED_REGISTER SC_INDEX,MAP_MASK,0fh
				;enable writes to all planes, so
				; we can clear 4 pixels at a time
	mov	ax,VGA_SEGMENT
	mov	es,ax
	sub	di,di
	mov	ax,di
	mov	cx,8000h	;# of words in 64K
	cld
	rep	stosw		;clear all of display memory
;
; Tweak the mode to 320x400 256-color mode by not scanning each
; line twice.
;
	mov	dx,CRTC_INDEX
    IF 1			; the following code can be disabled to test memory access in 320x200 mode -JP
	mov	al,MAX_SCAN_LINE
	out	dx,al
	inc	dx
	in	al,dx
	and	al,not 1fh	;set maximum scan line = 0
	out	dx,al
	dec	dx
    ENDIF
;
; Change CRTC scanning from doubleword mode to byte mode, allowing
; the CRTC to scan more than 64K of video data.
;
	mov	al,UNDERLINE
	out	dx,al
	inc	dx
	in	al,dx
	and	al,not 40h	;turn off doubleword
	out	dx,al
	dec	dx
	mov	al,MODE_CONTROL
	out	dx,al
	inc	dx
	in	al,dx
	or	al,40h		;turn on the byte mode bit, so memory is
				; scanned for video data in a purely
				; linear way, just as in modes 10h and 12h
	out	dx,al
	ret
Set320By400Mode endp
;
; Draws a pixel in the specified color at the specified
; location in 320x400 256-color mode.
;
; Input:
;	CX = X coordinate of pixel
;	DX = Y coordinate of pixel
;	BL = pixel color
;
; Output: none
;
; Registers altered: AX, CX, DX, DI, ES
;
WritePixel	proc	near
	mov	ax,VGA_SEGMENT
	mov	es,ax		;point to display memory
	mov	ax,SCREEN_WIDTH/4
				;there are 4 pixels at each address, so
				; each 320-pixel row is 80 bytes wide
				; in each plane
	mul	dx		;point to start of desired row
	push	cx		;set aside the X coordinate
	shr	cx,1		;there are 4 pixels at each address
	shr	cx,1		; so divide the X coordinate by 4
	add	ax,cx		;point to the pixel's address
	mov	di,ax
	pop	cx		;get back the X coordinate
	and	cl,3		;get the plane # of the pixel
	mov	ah,1
	shl	ah,cl		;set the bit corresponding to the plane
				; the pixel is in
	mov	al,MAP_MASK
	mov	dx,SC_INDEX
	OUT_WORD		;set to write to the proper plane for
				; the pixel
	mov	es:[di],bl	;draw the pixel
	ret
WritePixel	endp
;
; Reads the color of the pixel at the specified location in 320x400
; 256-color mode.
;
; Input:
;	CX = X coordinate of pixel to read
;	DX = Y coordinate of pixel to read
;
; Output:
;	AL = pixel color
;
; Registers altered: AX, CX, DX, SI, ES
;
ReadPixel	proc	near
	mov	ax,VGA_SEGMENT
	mov	es,ax		;point to display memory
	mov	ax,SCREEN_WIDTH/4
				;there are 4 pixels at each address, so
				; each 320-pixel row is 80 bytes wide
				; in each plane
	mul	dx		;point to start of desired row
	push	cx		;set aside the X coordinate
	shr	cx,1		;there are 4 pixels at each address
	shr	cx,1		; so divide the X coordinate by 4
	add	ax,cx		;point to the pixel's address
	mov	si,ax
	pop	ax		;get back the X coordinate
	and	al,3		;get the plane # of the pixel
	mov	ah,al
	mov	al,READ_MAP
	mov	dx,GC_INDEX
	OUT_WORD		;set to read from the proper plane for
				; the pixel
	lods	byte ptr es:[si];read the pixel
	ret
ReadPixel	endp
;
; Waits for the next key and returns it in AX.
;
; Input: none
;
; Output:
;	AX = full 16-bit code for key pressed
;
GetNextKey	proc	near
WaitKey:
	mov	ah,1
	int	16h
	jz	WaitKey		;wait for a key to become available
	sub	ah,ah
	int	16h		;read the key
	ret
GetNextKey	endp
;
Code	ends
;
	end	Start
