;
;  EGA GRAPHICS DRIVER 
;
;  supports raster graphics on the Enhanced Adapter
;
;      Tim Krauskopf               Spring 1986
;
;  National Center for Supercomputing Applications
;  605 E. Springfield
;  Champaign, IL 61820
;  (217) 244-0074
;
;
		TITLE   EGA GRAPHICS RASTER DRIVER
    	NAME	EGA
		INCLUDE DOS.MAC
		SETX
;
;  Define where the registers are
;
SEQ		EQU		03C4H
MAPREG	EQU		2
MAPMASK	EQU		03C5H
GCHIP	EQU		03CEH
DRREG   EQU     3
MODEREG EQU     5
BITREG	EQU		8
BITMASK	EQU		03CFH
;
		DSEG
FLAG    DB      0            ; HAVE WE SET REGS UP
		ENDDS

		PSEG
		PUBLIC  EGALINE;,EGALINE2,EGALINEA,EGAPOINT
		PUBLIC	INITEGA,RESETEGA,EGAPAL
;******************************************************************
;  INITEGA
;    use the BIOS to set mode 10 on the EGA board
;
INITEGA	PROC	FAR
		MOV		AX,10H			; set to hi-res mode for EGA
		INT 	10h				; video
		RET
INITEGA	ENDP

RESETEGA	PROC	FAR
		MOV		AX,3			; set to color character mode
		INT		10h
		RET
RESETEGA	ENDP

;*******************************************************************
;  PALETTE 
;     set one element of the EGA's palette select
;
;  usage:  egapal(reg,setting);
;             reg (0-15)        EGA's palette regs
;             setting			one byte, what to write there
;
EGAPAL	PROC	FAR
		PUSH	BP
		MOV		BP,SP
		MOV		BL,[BP+X]		; register #
		MOV		BH,[BP+X+2]		; value
		MOV		AX,01000H		; set palette element
		INT		10h
		POP		BP
		RET
EGAPAL	ENDP

ifdef QAK
;*****************************************************************
;  Puts an absolute pixel in the given color
;
;   usage:   egapoint(x,y,color,table)
;            x,y = point location
;            color = integer color number  0 to 255
;            table = translation table of 256 bytes for XLAT
;
;   BUG:  will only write on black background, see egaline for
;         clearing all four planes before writing to specified planes
;         in that color
;
EGAPOINT	PROC	FAR
		PUSH	BP
		MOV		BP,SP
		MOV     AL,FLAG  
		OR      AL,AL
		JNZ     NOSETUP
		CALL	SETUPG            ; set up the mask regs
        MOV     FLAG,1            ; set the flag
NOSETUP:
		MOV     CX,[BP+X+8]       ; get segment of transtable
        MOV     BX,[BP+X+6]       ; get offset of transtable
		MOV		AX,[BP+X+4]       ; get the color param = 3rd param
		PUSH DS
		MOV  DS,CX                ; set ds for the transtable
        XLATB                     ; pick up translated color
        POP     DS
        MOV		DX,MAPMASK
		OUT		DX,AL             ; set the mask to this color
		MOV		AX,0A000H
		MOV     ES,AX             ; set for addressing
        MOV		AX,[BP+X+2]       ; get y value 0 < y < 350
		MOV		BX,80
        MUL     BX                ; multiply by 80 bytes/line
        MOV     DI,AX             ; put in DI
        MOV     AX,[BP+X]         ; get x value 0 < x < 640
        MOV     BX,AX             ; save it
		MOV     CL,3
        SHR     AX,CL             ; divide by 8
		ADD     DI,AX             ; add it to di = byte offset
		;
		MOV     CX,BX
		AND     CX,0007H          ; save only the right three bits
		MOV     BL,80H            ; here's a bit
		SHR		BL,CL             ; move it over some
		MOV     DX,BITMASK        ; set the mask
		MOV     AL,BL             
        OUT     DX,AL             ;  send it
;
        MOV		AL,0FFH           ; set all bits
        MOV		AH,ES:[DI]        ; latch the bytes
        STOSB                     ; place that bit on bit mask

        
		POP		BP
		RET
EGAPOINT	ENDP
endif

SETUPG:
		MOV		DX,GCHIP
		MOV		AL,MODEREG
		OUT		DX,AL             ; choose write mode 0
		INC		DX
		MOV		AL,0
		OUT		DX,AL             ; clear mode reg to 0
		DEC     DX
		MOV		AL,DRREG          ; clear function select = Replace
        OUT		DX,AL
		INC     DX
		MOV		AL,0h             ; clear all
		OUT		DX,AL
;
		MOV		AL,MAPREG         ; want to access mapreg from 
		MOV		DX,SEQ            ; sequencer set of regs
		OUT		DX,AL
		MOV		AL,BITREG         ; want to access bitreg from
        MOV     DX,GCHIP          ; graphics chip's regs
		OUT		DX,AL
		MOV		DX,BITMASK        ; set up the bitmask = no masking
		MOV		AL,0FFH           ; all bits on
		OUT		DX,AL
		RET
;
;**************************************************************************
;  EGALINE
;
;   Write a stream of colors (represented by bytes) to the EGA, with 
;   a translation table for the EGA color map.
;
;   Usage:     egaline(x,y,colorbuffer,n,table,xoff)
;            x,y = point to start line
;            colorbuffer = 4 byte pointer to stream of 
;            n    bytes.
;            table  = 4 byte pointer to translation table, 256 bytes
;                      long for XLAT instruction
;			 xoff	= offset into the line to be sent to the screen

EGALINE	PROC	FAR
		PUSH	BP
		MOV		BP,SP
		MOV     AL,FLAG  
		OR      AL,AL
		JNZ     NOSETUP2
		CALL	SETUPG            ; set up the mask regs
        MOV     FLAG,1            ; set the flag
NOSETUP2:
		PUSH    DS                ; save our ds
		MOV     AX,[BP+X+6]       ; get color table segment
        MOV     DS,AX             ; use it now

		MOV		AX,0A000H
		MOV     ES,AX             ; set for addressing
		MOV		AX,[BP+X+14]; GET THE X OFFSET INTO THE ARRAY
		CMP		AX,0		; CHECK FOR NEGATIVE OFFSET
		JGE		OKSIGN		; JUMP AROUND FIXING THE WINDOW IF POSITIVE
		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+8]	; REDUCE THE NUMBER OF BYTES TO COPY TO THE SCREEN
		MOV		[BP+X+8],AX	; AND STORE THE NUMBER OF BYTES AGAIN
OKSIGN:
		MOV		[BP+X+14],AX; PUT THE OFFSET BACK
;
        MOV		AX,[BP+X+2]       ; get y value 0 < y < 350
		MOV		BX,80
        MUL     BX                ; multiply by 80 bytes/line
        MOV     DI,AX             ; put in DI
        MOV     AX,[BP+X]         ; get x value 0 < x < 640
        MOV     BX,AX             ; save it
		MOV     CL,3
        SHR     AX,CL             ; divide by 8
		ADD     DI,AX             ; add it to di = byte offset
		;
		MOV     CX,BX
		AND     CX,0007H          ; save only the right three bits
		MOV     BL,080H           ; here's a bit
		ROR		BL,CL             ; move it over some
;
;  At this point, bl has the bit mask for the first bit in the line
;  Keep rotating it and set the mask for each bit to write
;
		MOV     CX,[BP+X+8]       ; get count of bytes
		CMP		CX,0			  ; CHECK FOR ZERO BYTES
		JL		LINEDONE		  ; JUMP TO THE END OF THE ROUTINE
		MOV     SI,[BP+X+4]       ; where to get the new colors
		ADD 	SI,[BP+X+14]	  ; add in the x offset into the line

		MOV     AH,BL             ; keep bit rotation in ah
		MOV     DX,[BP+X+12]      ; get segment of transtable
        MOV     BX,[BP+X+10]      ; get offset of transtable

NEXTBIT:
		LODSB                     ; get the next color to al
		PUSH DS
		MOV  DS,DX                ; set ds for the transtable
        XLATB                     ; pick up translated color to al
		PUSH AX                   ; need it later

		MOV     DX,BITMASK        ; set the bitmask to current rotation
		MOV     AL,AH
        OUT     DX,AL             ;  send it

        MOV		DX,MAPMASK
		MOV     AL,0FH            ; open all bit planes
		OUT		DX,AL             ; send it

		MOV		AL,ES:[DI]        ; latch the whole set of bytes
		MOV     BYTE PTR ES:[DI],0H  ; clear all four planes to 0 at this bit
		POP		AX                ; get real color back
		OUT		DX,AL			  ; send it to set the color planes

		MOV     DX,DS             ;  recover what was in dx
        POP     DS
;
        MOV		AL,0FFH           ; set all bits
		CMP     AH,01             ; see if we are at end of byte
        JZ      INCSTORE

NONINC:
        MOV     ES:[DI],AL        ; write the bit from bitmask
		ROR     AH,1              ; rotate me
		LOOP    NEXTBIT          ; go back for next one
		JMP     LINEDONE

INCSTORE:
        STOSB                     ; place that bit on bit mask and next byte
		ROR     AH,1              ; rotate me
		LOOP    NEXTBIT

LINEDONE:
		POP     DS        
		POP		BP
		RET
EGALINE	ENDP

ifdef QAK		
EGALINE2	PROC	FAR
		PUSH	BP
		MOV		BP,SP
		MOV     AL,FLAG  
		OR      AL,AL
		JNZ     NO2SETUP2
		CALL	SETUPG            ; set up the mask regs
        MOV     FLAG,1            ; set the flag
NO2SETUP2:
		PUSH    DS                ; save our ds
		MOV     AX,[BP+X+6]       ; get color table segment
        MOV     DS,AX             ; use it now

		MOV		AX,0A000H
		MOV     ES,AX             ; set for addressing
        MOV		AX,[BP+X+2]       ; get y value 0 < y < 350
		MOV		BX,80
        MUL     BX                ; multiply by 80 bytes/line
        MOV     DI,AX             ; put in DI
        MOV     AX,[BP+X]         ; get x value 0 < x < 640
        MOV     BX,AX             ; save it
		MOV     CL,3
        SHR     AX,CL             ; divide by 8
		ADD     DI,AX             ; add it to di = byte offset
		;
		MOV     CX,BX
		AND     CX,0007H          ; save only the right three bits
		MOV     BL,0C0H           ; here's 2 bits
		ROR		BL,CL             ; move it over some
;
;  At this point, bl has the bit mask for the first bit in the line
;  Keep rotating it and set the mask for each bit to write
;
		MOV     CX,[BP+X+8]       ; get count of bytes
		MOV     SI,[BP+X+4]       ; where to get the new colors

		MOV     AH,BL             ; keep bit rotation in ah
		MOV     DX,[BP+X+12]      ; get segment of transtable
        MOV     BX,[BP+X+10]      ; get offset of transtable

NEXT2BIT:
		LODSB                     ; get the next color to al
		PUSH DS
		MOV  DS,DX                ; set ds for the transtable
        XLATB                     ; pick up translated color to al
		PUSH AX                   ; need it later

		MOV     DX,BITMASK        ; set the bitmask to current rotation
		MOV     AL,AH
        OUT     DX,AL             ;  send it

        MOV		DX,MAPMASK
		MOV     AL,0FH            ; open all bit planes
		OUT		DX,AL             ; send it

		MOV		AL,ES:[DI]        ; latch the whole set of bytes
		MOV     BYTE PTR ES:[DI],0H  ; clear all four planes to 0 at this bit
		POP		AX                ; get real color back
		OUT		DX,AL			  ; send it to set the color planes

		MOV     DX,DS             ;  recover what was in dx
        POP     DS
;
        MOV		AL,0FFH           ; set all bits
		CMP     AH,03             ; see if we are at end of byte
        JZ      INC2STORE

NON2INC:
        MOV     ES:[DI],AL        ; write the bit from bitmask
		ROR     AH,1              ; rotate me
		ROR     AH,1              ; rotate me
		LOOP    NEXT2BIT          ; go back for next one
		JMP     LINE2DONE

INC2STORE:
        STOSB                     ; place that bit on bit mask and next byte
		ROR     AH,1              ; rotate me
        ROR     AH,1              ; again
		LOOP    NEXT2BIT

LINE2DONE:
		POP     DS        
		POP		BP
		RET
EGALINE2	ENDP

;**************************************************************************
;  EGALINEA
;
;   Write a stream of colors (represented by bytes) to the EGA, with 
;   a translation table for the EGA color map.  This one includes arbitrary
;   width expansion.
;
;   Usage:     egalinea(x,y,colorbuffer,n,table,expansion)
;            x,y = point to start line
;            colorbuffer = 4 byte pointer to stream of 
;            n    bytes.
;            table  = 4 byte pointer to translation table, 256 bytes
;                      long for XLAT instruction
;            expansion = how much horizontal pixel expansion you want
;

EGALINEA	PROC	FAR
		PUSH	BP
		MOV		BP,SP
		MOV     AL,FLAG  
		OR      AL,AL
		JNZ     NOSETA
		CALL	SETUPG            ; set up the mask regs
        MOV     FLAG,1            ; set the flag
NOSETA:
		PUSH    DS                ; save our ds
		MOV     AX,[BP+X+6]       ; get color table segment
        MOV     DS,AX             ; use it now

		MOV		AX,0A000H
		MOV     ES,AX             ; set for addressing
        MOV		AX,[BP+X+2]       ; get y value 0 < y < 350
		MOV		BX,80
        MUL     BX                ; multiply by 80 bytes/line
        MOV     DI,AX             ; put in DI

        MOV     AX,[BP+X]         ; get x value 0 < x < 640
        MOV     BX,AX             ; save it
		MOV     CL,3
        SHR     AX,CL             ; divide by 8
		ADD     DI,AX             ; add it to di = byte offset
		;
		MOV     CX,BX
		AND     CX,0007H          ; save only the right three bits
		MOV     BL,080H           ; here's a bit
		ROR		BL,CL             ; move it over some
;
;  At this point, bl has the bit mask for the first bit in the line
;  Keep rotating it and set the mask for each bit to write
;
		MOV     SI,[BP+X+4]       ; where to get the new colors

		MOV     AH,BL             ; keep bit rotation in ah
		MOV     DX,[BP+X+12]      ; get segment of transtable
        MOV     BX,[BP+X+10]      ; get offset of transtable

NEXTBYTE:
		MOV		CX,[BP+X+14]      ; pixel expansion number
		LODSB                     ; the next color to write
        PUSH 	DS
 		MOV		DS,DX             ; get the seg for xlat from dx
		XLATB					  ; color translation
		MOV		[BP+X],AL         ; used as a local var for translated color

NEXTP:
		MOV     DX,BITMASK        ; set the bitmask to current rotation
		MOV     AL,AH
        OUT     DX,AL             ;  send it

        MOV		DX,MAPMASK
		MOV     AL,0FH            ; open all bit planes
		OUT		DX,AL             ; send it

		MOV		AL,ES:[DI]        ; latch the whole set of bytes
		MOV     BYTE PTR ES:[DI],0H  ; clear all four planes to 0 at this bit
		MOV		AL,[BP+X]         ; get real color from local var
		OUT		DX,AL			  ; send it to set the color planes
;
        MOV		AL,0FFH           ; set all bits
		MOV		ES:[DI],AL	      ; set it!
		CMP     AH,01             ; see if we are at end of byte
        JNZ     SAMEBYTE
		INC		DI                ; get us to the next destination byte
SAMEBYTE:
		ROR     AH,1              ; rotate me
		LOOP    NEXTP             ; go back for next one

		MOV		DX,DS             ; reset xlat segment into dx
		POP		DS                ; get data input seg back
		DEC		WORD PTR [BP+X+8] ; number of data points
		JNZ		NEXTBYTE          ; do another, if necessary

		POP     DS        
		POP		BP
		RET
EGALINEA	ENDP
endif
		ENDPS
		END
