;	
;	VGA support routines for raster oriented graphics
;
;	Quincey Koziol		June 1988
;
;	National Center for Supercomputing Applications,	University of Illinois
;	153	Water Resources Building
;	605 E. Springfield Ave.
;	Champaign, Ill	61820
;	(217)244-0072
;
	TITLE      VGA GRAPHICS SUPPORT
	INCLUDE DOS.MAC
	SETX
	PSEG
	PUBLIC	PUTMAPV,GETMAP,VGALINE1,VGAMODE;,VGAOFF,VGAON,VGAPT
	PUBLIC	SHOWPALV,NOPALV,OUTLINEV
;
;	take three arrays of color tables and interleave them into the
;	VGA registers in the fashion Red, Green, Blue.
;

PUTMAPV	PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES
;
	MOV	AX,[BP+X+2]
	MOV	DS,AX			; WHERE TO GET MAPS (SEG)
	MOV	SI,[BP+X]		; WHERE TO GET RED
	MOV DI,[BP+X+4]		; WHERE TO GET GREEN
	MOV BP,[BP+X+8]		; WHERE TO GET BLUE
	MOV	DX,03C8H		; I/O PORT FOR PEL TABLE
	MOV AL,0			; VALUE FOR BEGINNING OF TABLE
	OUT DX,AL			; START OUTPUTTING VALUES
	INC DX				; INCREMENT DX TO OUTPUT TABLE
	MOV CX,256			; LENGTH OF THE TABLE IN BYTE TRIPLES

DORGB:
	MOV AL,DS:[SI]		; GET A RED BYTE
	SHR AL,1
	SHR AL,1			; GET RID OF TWO HIGHEST BITS
	OUT DX,AL			
	INC SI
;
	MOV AL,DS:[DI]		; GET A GREEN BYTE
	SHR	AL,1
	SHR AL,1			; GET RID OF TWO HIGHEST BITS
	OUT DX,AL
	INC DI
;
	MOV	AL,DS:[BP]		; GET A BLUE BYTE
	SHR AL,1
	SHR AL,1			; GET RID OF TWO HIGHEST BITS
	OUT DX,AL			
	INC BP
;
	LOOP DORGB			; CONTINUE PRINTING UNTIL ALL 768 BYTES ARE WRITTEN
;
	POP	ES
	POP	DS
	POP	BP
	RET

PUTMAPV	ENDP

GETMAP	PROC	FAR
	RET
GETMAP	ENDP

;
;  Transfer line to vga screen
;
;   usage : vgaline1(x,y,buf,xoff,linelen)
;
VGALINE1	PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES

OKBANK:
	MOV	AX,0A000H	; DATA BUFFER
	MOV	ES,AX
	MOV	SI,[BP+X+4]	; WHERE DATA WILL COME FROM
	MOV	AX,[BP+X+8]	; GET THE X OFFSET INTO THE ARRAY
	CMP	AX,0		; CHECK FOR NEGATIVE OFFSET
	JGE	OKSIGN		; JUMP AROUND FIXING THE WINDOW
	NEG	AX			; TAKE THE OPPOSITE VALUE
	ADD AX,[BP+X]	; AND ADD IT TO THE POSITION ON THE SCREEN
	MOV	[BP+X],AX	; AND RE-STORE THE POSITION
	MOV	AX,[BP+X+8]	; GET THE NEGATIVE OFFSET AGAIN
	ADD	AX,[BP+X+10]	; REDUCE THE NUMBER OF BYTES TO COPY TO THE SCREEN
	MOV	[BP+X+10],AX	; AND STORE THE NUMBER OF BYTES AGAIN
OKSIGN:
	ADD	SI,AX		; ADD THE OFFSET TO THE OFFSET OF THE ARRAY
;
	MOV AX,[BP+X+6]	; SEGMENT OF DATA
	MOV DS,AX
;
	MOV	AX,[BP+X+2]	; GET Y VALUE AGAIN
	MOV	DX,320		; GET LENGTH OF A LINE
	MUL	DX
	JA	DONTFIX		; IF DX IS ZERO THEN DON'T PLAY WITH THE SEGMENT
	MOV	BX,ES		; GET THE ES
	ADD	BX,DX		; INCREMENT THE ES
	MOV	ES,BX		; AND REPLACE IT
DONTFIX:
	ADD	AX,[BP+X]	; X VALUE OF WHERE ON SCREEN ADDED IN
	MOV	DI,AX		; PREPARE FOR MOVS
	MOV	CX,[BP+X+10]	; HOW MANY BYTES?
BLAST:
	REP	MOVSB

	POP	ES
	POP	DS
	POP	BP
	RET
VGALINE1	ENDP

ifdef QAK
;  point on vga screen
;
;   usage : vgapt(x,y,color)
;
VGAPT	PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES

;
;   READY TO PUT THE POINT
;
POKBANK:
	MOV	AX,0A000H	; DATA BUFFER
	MOV	ES,AX

	MOV	AX,[BP+X+2]	; GET Y VALUE AGAIN
	MOV	DX,320
	MUL	DX
	ADD	AX,[BP+X]	; X VALUE OF WHERE ON SCREEN ADDED IN

	MOV	DI,AX		; PREPARE FOR MOVS
	MOV	AL,[BP+X+4]	; GET COLOR TO PUT THERE

	STOSB			; PUT IT

	POP	ES
	POP	DS
	POP	BP
	RET
VGAPT	ENDP
endif

VGAMODE		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES
	
	MOV		AX,[BP+X]		;GET SCREEN MODE TO SWITCH TO
	MOV		AH,00H			;ENTER VIDEO_IO ROUTINE (SET MODE=0)
	INT		10H				;VIDEO INTERUPT

	POP	ES
	POP	DS
	POP	BP
	RET
VGAMODE	ENDP

ifdef QAK
VGAOFF		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES
	
	MOV		DX,03C4H		;ADDRESS OF THE SEQ CONTROLLER
	MOV		AL,01H			;INDEX OF THE CLOCKING MODE REGISTER
	OUT		DX,AL			;SET UP TO READ THE CLOCKING MODE REGISTER
	MOV		DX,03C5H		;ADDRESS TO READ FROM
	IN		AL,DX			;GET THE CLOCKING MODE REGISTER
	OR		AL,20H			;MASK OFF THE SCREEN ENABLE BIT	
	MOV		BL,AL			;KEEP THAT AROUND
	MOV		DX,03C4H		;ADDRESS OF THE SEQ CONTROLLER
	MOV		AL,01H			;INDEX OF THE CLOCKING REGISTER
	OUT		DX,AL			;SET UP TO WRITE BACK THE CLOCKING MODE REGISTER
	INC		DX				;SET TO ADDRESS TO WRITE TO
	MOV		AL,BL			;GET BACK THE PROPER VALUE
	OUT		DX,AL			;TURNS OFF THE VGA SCREEN

	POP	ES
	POP	DS
	POP	BP
	RET
VGAOFF	ENDP

VGAON		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES
	
	MOV		DX,03C4H		;ADDRESS OF THE SEQ CONTROLLER
	MOV		AL,01H			;INDEX OF THE CLOCKING MODE REGISTER
	OUT		DX,AL			;SET UP TO READ THE CLOCKING MODE REGISTER
	INC		DX				;ADDRESS TO READ FROM
	IN		AL,DX			;GET THE CLOCKING MODE REGISTER
	AND		AL,0DFH			;TURN ON THE SCREEN ENABLE BIT	
	MOV		BL,AL			;KEEP THAT AROUND
	MOV		DX,03C4H		;ADDRESS OF THE SEQ CONTROLLER
	MOV		AL,01H			;INDEX OF THE CLOCKING REGISTER
	OUT		DX,AL			;SET UP TO WRITE BACK THE CLOCKING MODE REGISTER
	INC		DX				;SET TO ADDRESS TO WRITE TO
	MOV		AL,BL			;GET BACK THE PROPER VALUE
	OUT		DX,AL			;TURNS ON THE VGA SCREEN

	POP	ES
	POP	DS
	POP	BP
	RET
VGAON	ENDP
endif

;
;	showpalv(&palstore,pal_xoff,pal_yoff);
;	
SHOWPALV		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES


	MOV 	AX,[BP+X]		;GET OFFSET OF ARRAY
	MOV		DI,AX
	MOV		AX,[BP+X+2]		;GET SEGMENT OF STORAGE ARRAY
	MOV		ES,AX			

	MOV		AX,[BP+X+6]		;GET LINE TO START PALETTE ON
	MOV		DX,320			;GET THE LINE LENGTH
	MUL		DX				;GET THE OFFSET INTO THE SCREEN
	ADD		AX,[BP+X+4]		;ADD IN THE X OFFSET
	JNC		NO_ROLLOVER		;ADDITION DIDN'T CARRY
	INC		DX				;INCREMENT THE DX REGISTER ON A CARRY
NO_ROLLOVER:
	MOV		SI,AX			;KEEP TRACK OF THE OFFSET
	MOV		BX,SI			;IN TWO PLACES
	MOV		AX,320			;GET THE LINE LENGTH
	MOV		DX,8			;GET THE NUMBER OF LINES
	MUL		DX				;GET THE NUMBER OF BYTES TO COPY
	MOV		CX,AX			;MOVE INTO COUNTER
	MOV		AX,0A000H
	MOV		DS,AX			;GET THE SEGMENT FOR THE SOURCE
ZIP:
	REP		MOVSB			;COPY THAT SECTION OF SCREEN
	
	MOV		CH,0			;THE NUMBER OF LINES COPIED SO FAR
	MOV		CL,00			;THE NUMBER OF PIXELS
TOP1:
	MOV		SI,BX			;GET THE CORRECT OFFSET TO BE COPIED TO
	MOV		AX,0A000H
	MOV		ES,AX			;GET THE SEGMENT TO BE COPIED TO
TOP2:
	MOV		ES:[SI],CL		;COPY ACROSS THE SCREEN INCREMENTING VALUES
	INC		SI				;MOVE TO NEXT PIXEL
	INC		CL				;INCREMENT COUNTER
	JNE		TOP2			;IF COUNTER NOT EQUAL TO ZERO THEN COPY AGAIN
	
	ADD		BX,320			;MOVE DOWN TO NEXT LINE
	INC		CH				;INCREMENT THE LINE COUNTER
	CMP		CH,8			;CHECK IF ALL THE LINES ARE DONE
	JNE		TOP1			;IF LINE COUNT <8 THEN COPY ANOTHER LINE

	POP	ES
	POP	DS
	POP	BP
	RET
SHOWPALV	ENDP

;
;	nopalv(&palstore,pal_xoff,pal_yoff);
;
NOPALV		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES

	MOV 	AX,[BP+X]		;GET OFFSET OF ARRAY
	MOV		SI,AX
	MOV		AX,[BP+X+2]		;GET SEGMENT OF STORAGE ARRAY
	MOV		DS,AX			

	MOV		AX,[BP+X+6]		;GET LINE TO COPY SCREEN BACK ONTO
	MOV		DX,320			;GET THE LINE LENGTH
	MUL		DX				;GET THE OFFSET INTO THE SCREEN
	ADD		AX,[BP+X+4]		;ADD IN THE X OFFSET
	JNC		NO_ROLL2		;ADDITION DIDN'T CARRY
	INC		DX				;INCREMENT THE DX REGISTER ON A CARRY
NO_ROLL2:
	MOV		DI,AX			;KEEP TRACK OF THE OFFSET
	MOV		AX,320			;GET THE LINE LENGTH
	MOV		DX,8			;GET THE NUMBER OF LINES
	MUL		DX				;GET THE NUMBER OF BYTES TO COPY
	MOV		CX,AX			;MOVE INTO COUNTER
	MOV		AX,0A000H
	MOV		ES,AX			;GET THE SEGMENT FOR THE SOURCE
ZOOM:
	REP		MOVSB			;COPY THAT SECTION OF SCREEN

	POP	ES
	POP	DS
	POP	BP
	RET
NOPALV	ENDP

;
;  Invert A BOX ON THE SCREEN
;
;   usage : outlinev(x1,y1,x2,y2)
;
OUTLINEV	PROC	FAR
	PUSH	BP
	MOV	BP,SP
	PUSH	DS
	PUSH	ES

	MOV	AX,[BP+X+2]	; GET FIRST Y VALUE
	CMP	AX,[BP+X+6]	; COMPARE WITH THE SECOND Y - VALUE
	JLE	CHECKX		; OK ORDER GOTO CHECKING THE X VALUES
	MOV	CX,AX		; SWAP THE TWO VALUES
	MOV	AX,[BP+X+6]	;
	MOV	[BP+X+2],AX	;
	MOV	AX,CX		;
	MOV	[BP+X+6],AX	;
CHECKX:
	MOV	AX,[BP+X]	; GET FIRST X VALUE
	CMP	AX,[BP+X+4]	; COMPARE WITH THE SECOND X - VALUE
	JLE	MAKELEN		; OK ORDER GOTO COMPUTING THE LENGTHS
	MOV	CX,AX		; SWAP THE TWO VALUES
	MOV	AX,[BP+X+4]	;
	MOV	[BP+X],AX	;
	MOV	AX,CX		;
	MOV	[BP+X+4],AX	;
MAKELEN:			; COMPUTE THE X AND Y WIDTHS FOR THE BOX TO BE INVERTED
	MOV	AX,[BP+X+4]	; GET THE LARGER OF THE TWO X VALUES
	SUB	AX,[BP+X]	; SUBTRACT THE SMALLER VALUE TO FIND THE LENGTH
	ADD	AX,1		;
	MOV	[BP+X+4],AX	; STORE IT IN THE OLD LOCATION FOR THE 2ND X VALUE
	MOV	AX,[BP+X+6]	; GET THE LARGER OF THE TWO Y VALUES
	SUB	AX,[BP+X+2]	; SUBTRACT THE SMALLER VALUE TO FIND THE LENGTH
	SUB	AX,1		; SUBTRACT TWO FOR THE TOP AND BOTTOM EDGES
	CMP	AX,0		; CHECK IF IT IS LESS THAN ZERO
	JG 	POSITIVE	; JUMP AROUND ZEROING THE ACC.
	MOV	AX,0
POSITIVE:
	MOV	[BP+X+6],AX	; STORE IT IN THE OLD LOCATION FOR THE 2ND Y VALUE
	MOV	AX,320		; COMPUTE THE VALUE TO ADD TO THE DI FOR COMPLETE WRAPAROUND
	SUB	AX,[BP+X+4]	;
	MOV	SI,AX		; KEEP TRACK OF IT
	
	MOV	AX,0A000H	; DATA BUFFER
	MOV	ES,AX
;
	MOV	AX,[BP+X+2]	; GET Y VALUE AGAIN
	MOV	DX,320		; GET LENGTH OF A LINE
	MUL	DX
	JA	DONTFIXA	; IF DX IS ZERO THEN DON'T PLAY WITH THE SEGMENT
	MOV	BX,ES		; GET THE ES
	ADD	BX,DX		; INCREMENT THE ES
	MOV	ES,BX		; AND REPLACE IT
DONTFIXA:
	ADD	AX,[BP+X]	; X VALUE OF WHERE ON SCREEN ADDED IN
	MOV	DI,AX		; PREPARE FOR MOVS
	MOV	CX,[BP+X+4]	; HOW MANY BYTES?
BLASTA:
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL		; INVERT THE VALUE THERE
	MOV	ES:[DI],AL	; REPLACE THE VALUE
	INC	DI		; INCREMENT TO NEXT POSITION
	LOOP 	BLASTA		; LOOP UNTIL ALL HAVE BEEN DONE

	ADD	DI,SI		; MOVE OVER TO THE BEGINNING OF THE NEXT LINE
	MOV	CX,[BP+X+6]	; COUNT VALUE FOR THE NEXT SERIES
	CMP	CX,0		; CHECK FOR NO LINES IN BETWEEN
	JLE	BLAST2A		; JUMP AROUND PUTTING THE LINES IN BETWEEN
BLAST2:
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE INVERTED VALUE
	ADD	DI,[BP+X+4]	; GO TO THE LAST VALUE TO INVERT ON THAT LINE
	SUB	DI,1		;
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL		; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE INVERTED VALUE
	ADD	DI,SI		; GET THE VALUE FOR THE BEGINNING OF THE NEXT LINE
	INC	DI		;
	LOOP	BLAST2		; DO THE NEXT LINE

BLAST2A:
	MOV	CX,[BP+X+4]	; PREPARE FOR LAST LINE
BLAST3:
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT THE VALUE THERE
	MOV	ES:[DI],AL	; REPLACE THE VALUE
	INC	DI			; INCREMENT TO NEXT POSITION
	LOOP	BLAST3		; LOOP UNTIL ALL HAVE BEEN DONE

	POP	ES
	POP	DS
	POP	BP
	RET
OUTLINEV	ENDP
;
;  Draw a 4x4 ball on the vga screen
;
;   usage : ball(x,y)
;
BALL		PROC	FAR
	PUSH	BP
	MOV		BP,SP
	PUSH	DS
	PUSH	ES

	MOV	AX,0A000H	; DATA BUFFER
	MOV	ES,AX
;
	MOV	AX,[BP+X+2]	; GET Y VALUE
	MOV	DX,320		; GET LENGTH OF A LINE
	MUL	DX
	JA	DONTFIXB	; IF DX IS ZERO THEN DON'T PLAY WITH THE SEGMENT
	MOV	BX,ES		; GET THE ES
	ADD	BX,DX		; INCREMENT THE ES
	MOV	ES,BX		; AND REPLACE IT
DONTFIXB:
	ADD	AX,[BP+X]	; X VALUE OF WHERE ON SCREEN ADDED IN
	MOV	DI,AX		; PREPARE FOR MOVS
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE VALUE
	INC	DI			; MOVE OVER ONE BYTE
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE VALUE

	ADD	DI,318		; MOVE TO THE NEXT LINE
	MOV	CX,4		; HOW MANY BYTES?
BLASTB:
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT THE VALUE THERE
	MOV	ES:[DI],AL	; REPLACE THE VALUE
	INC	DI			; INCREMENT TO NEXT POSITION
	LOOP BLASTB		; LOOP UNTIL ALL HAVE BEEN DONE

	MOV	CX,4		; COUNT VALUE FOR THE NEXT SERIES
	ADD	DI,316		; MOVE OVER TO THE BEGINNING OF THE NEXT LINE
BLASTC:
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE INVERTED VALUE
	INC DI			;
	LOOP	BLASTC	; DO THE NEXT LINE

	ADD	DI,317		; MOVE ONE BYTE OVER INTO THE NEXT LINE
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE VALUE
	INC	DI			; MOVE OVER ONE BYTE
	MOV	AL,ES:[DI]	; GET THE VALUE TO INVERT
	NOT	AL			; INVERT IT
	MOV	ES:[DI],AL	; REPLACE THE VALUE

	POP	ES
	POP	DS
	POP	BP
	RET
BALL	ENDP

	ENDPS
	END
