PAGE 118,121
TITLE VIDEO1 --- 06/10/85  VIDEO DISPLAY BIOS
.286C
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	ACT_DISP_PAGE
	PUBLIC	READ_AC_CURRENT
	PUBLIC	READ_CURSOR
	PUBLIC	READ_DOT
	PUBLIC	READ_LPEN
	PUBLIC	SCROLL_DOWN
	PUBLIC	SCROLL_UP
	PUBLIC	SET_COLOR
	PUBLIC	SET_CPOS
	PUBLIC	SET_CTYPE
	PUBLIC	SET_MODE
	PUBLIC	WRITE_AC_CURRENT
	PUBLIC	WRITE_C_CURRENT
	PUBLIC	WRITE_DOT
	PUBLIC	WRITE_TTY
	PUBLIC	VIDEO_IO_1
	PUBLIC	VIDEO_STATE

	EXTRN	BEEP:NEAR	; SPEAKER BEEP ROUTINE
	EXTRN	CRT_CHAR_GEN:NEAR	; CHARACTER GENERATOR GRAPHICS TABLE
	EXTRN	DSS:NEAR	; LOAD (DS) WITH DATA SEGMENT SELECTOR
	EXTRN	M5:WORD	; REGEN BUFFER LENGTH TABLE
	EXTRN	M6:BYTE	; COLUMNS PER MODE TABLE
	EXTRN	M7:BYTE	; MODE SET VALUE PER MODE TABLE

;--- INT 10 H -------------------------------------------------------------------
; VIDEO_IO 			:	
;	THESE ROUTINES PROVIDE THE CRT DISPLAY INTERFACE 	:
;	THE FOLLOWING FUNCTIONS ARE PROVIDED:	:
;				:
;    (AH)= 00H	SET MODE (AL) CONTAINS MODE VALUE	:
;		(AL) = 00H  40X25 BW MODE (POWER ON DEFAULT)	:
;		(AL) = 01H  40X25 COLOR		:
;		(AL) = 02H  80X25 BW		:
;		(AL) = 03H  80X25 COLOR		:
;		              GRAPHICS MODES	:
;		(AL) = 04H  320X200 COLOR	:
;		(AL) = 05H  320X200 BW MODE	:
;		(AL) = 06H  640X200 BW MODE	:
;		(AL) = 07H   80X25 MONOCHROME (USED INTERNAL TO VIDEO ONLY)	:
;		*** NOTES -BW MODES OPERATE SAME AS COLOR MODES, BUT COLOR	:
;		           BURST IS NOT ENABLED	:
;		          -CURSOR IS NOT DISPLAYED IN GRAPHICS MODE	:
;    (AH)= 01H	SET CURSOR TYPE		:
;		(CH) = BITS 4-0 = START LINE FOR CURSOR	:
;		       ** HARDWARE WILL ALWAYS CAUSE BLINK	:
;		       ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC BLINKING	:
;		          OR NO CURSOR AT ALL	:
;		(CL) = BITS 4-0 = END LINE FOR CURSOR	:
;    (AH)= 02H	SET CURSOR POSITION		:
;		(DH,DL) = ROW,COLUMN  (00H,00H) IS UPPER LEFT	:
;		(BH) = A PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)	:
;    (AH)= 03H	READ CURSOR POSITION		:
;		(BH) = PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)	:
;		ON EXIT (DH,DL) = ROW,COLUMN OF CURRENT CURSOR	:
;		        (CH,CL) = CURSOR MODE CURRENTLY SET	:
;    (AH)= 04H	READ LIGHT PEN POSITION		:
;		ON EXIT:		:
;		(AH) = 00H -- LIGHT PEN SWITCH NOT DOWN/NOT TRIGGERED	:
;		(AH) = 01H -- VALID LIGHT PEN VALUE IN REGISTERS	:
;		        (DH,DL) = ROW,COLUMN OF CHARACTER LP POSITION	:
;		        (CH) = RASTER LINE (0-199)	:
;		        (BX) = PIXEL COLUMN (0-319,639)	:
;    (AH)= 05H	SELECT ACTIVE DISPLAY PAGE (VALID ONLY FOR ALPHA MODES)	:
;		(AL) = NEW PAGE VALUE (0-7 FOR MODES 0&1, 0-3 FOR MODES 2&3)	:
;    (AH)= 06H	SCROLL ACTIVE PAGE UP		:
;		(AL) = NUMBER OF LINES. ( LINES BLANKED AT BOTTOM OF WINDOW )	:
;		        (AL) = 00H MEANS BLANK ENTIRE WINDOW	:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE	:
;    (AH)= 07H	SCROLL ACTIVE PAGE DOWN		:
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT TOP OF WINDOW	:
;		        (AL) = 00H MEANS BLANK ENTIRE WINDOW	:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE	:
;				:
;   CHARACTER HANDLING ROUTINES		:
;				:
;    (AH)= 08H	READ ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		ON EXIT:		:
;		(AL) = CHAR READ		:
;		(AH) = ATTRIBUTE OF CHARACTER READ (ALPHA MODES ONLY)	:
;    (AH)= 09H	WRITE ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		(CX) = COUNT OF CHARACTERS TO WRITE	:
;		(AL) = CHAR TO WRITE		:
;		(BL) = ATTRIBUTE OF CHARACTER (ALPHA)/COLOR OF CHAR (GRAPHICS)	:
;		         SEE NOTE ON WRITE DOT FOR BIT 7 OF BL = 1.	:
;    (AH) = 0AH	WRITE CHARACTER ONLY AT CURRENT CURSOR POSITION	:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	:
;		(CX) = COUNT OF CHARACTERS TO WRITE	:
;		(AL) = CHAR TO WRITE		:
;		       NOTE: USE FUNCTION (AH)= 09H IN GRAPHICS MODES	:
;	FOR READ/WRITE CHARACTER INTERFACE WHILE IN GRAPHICS MODE, THE	:
;		CHARACTERS ARE FORMED FROM A CHARACTER GENERATOR IMAGE	:
;		MAINTAINED IN THE SYSTEM ROM. ONLY THE 1ST 128 CHARS	:
;		ARE CONTAINED THERE. TO READ/WRITE THE SECOND 128 CHARS,	:
;		THE USER MUST INITIALIZE THE POINTER AT INTERRUPT 1FH	:
;		(LOCATION 0007CH) TO POINT TO THE 1K BYTE TABLE CONTAINING	:
;		THE CODE POINTS FOR THE SECOND 128 CHARS (128-255).	:
;	FOR WRITE CHARACTER INTERFACE IN GRAPHICS MODE, THE REPLICATION FACTOR	:
;		CONTAINED IN (CX) ON ENTRY WILL PRODUCE VALID RESULTS ONLY	:
;		FOR CHARACTERS CONTAINED ON THE SAME ROW. CONTINUATION TO	:
;		SUCCEEDING LINES WILL NOT PRODUCE CORRECTLY.	:
;				:
;    GRAPHICS INTERFACE		:
;    (AH)= 0BH	SET COLOR PALETTE		:
;		(BH) = PALETTE COLOR ID BEING SET (0-127)	:
;		(BL) = COLOR VALUE TO BE USED WITH THAT COLOR ID	:
;		       NOTE: FOR THE CURRENT COLOR CARD, THIS ENTRY POINT HAS	:
;		               MEANING ONLY FOR 320X200 GRAPHICS.	:
;		       COLOR ID = 0 SELECTS THE BACKGROUND COLOR (0-15)	:
;		       COLOR ID = 1 SELECTS THE PALETTE TO BE USED:	:
;		               0 = GREEN(1)/RED(2)/YELLOW(3)	:
;		               1 = CYAN(1)/MAGENTA(2)/WHITE(3)	:
;		       IN 40X25 OR 80X25 ALPHA MODES, THE VALUE SET FOR 	:
;		               PALETTE COLOR 0 INDICATES THE BORDER COLOR	:
;		               TO BE USED (VALUES 0-31, WHERE 16-31 SELECT	:
;		               THE HIGH INTENSITY BACKGROUND SET.	:
;    (AH)= 0CH	WRITE DOT		:
;		(DX) = ROW NUMBER		:
;		(CX) = COLUMN NUMBER		:
;		(AL) = COLOR VALUE		:
;		        IF BIT 7 OF AL = 1, THEN THE COLOR VALUE IS EXCLUSIVE	:
;		        ORed WITH THE CURRENT CONTENTS OF THE DOT	:
;    (AH)= ODH	READ DOT		:
;		(DX) = ROW NUMBER		:
;		(CX) = COLUMN NUMBER		:
;		(AL) = RETURNS THE DOT READ	:
;				:
;    ASCII TELETYPE ROUTINE FOR OUTPUT		:
;				:
;    (AH)= 0EH	WRITE TELETYPE TO ACTIVE PAGE	:
;		(AL) = CHAR TO WRITE		:
;		(BL) = FOREGROUND COLOR IN GRAPHICS MODE	:
;		NOTE -- SCREEN WIDTH IS CONTROLLED BY PREVIOUS MODE SET	:
;    (AH)= 0FH	CURRENT VIDEO STATE		:
;		RETURNS THE CURRENT VIDEO STATE	:
;		(AL) = MODE CURRENTLY SET ( SEE (AH)=00H FOR EXPLANATION)	:
;		(AH) = NUMBER OR CHARACTER COLUMNS ON SCREEN	:
;		(BH) = CURRENT ACTIVE DISPLAY PAGE	:
;    (AH)= 10H	RESERVED		:
;    (AH)= 11H	RESERVED		:
;    (AH)= 12H	RESERVED		:
;    (AH)= 13H	WRITE STRING		:
;			ES:BP  -  POINTER T0 STRING TO BE WRITTEN	:
;			CX     -  LENGTH OF CHARACTER STRING TO WRITTEN	:
;			DX     -  CURSOR POSITION FOR STRING TO BE WRITTEN	:
;			BH     -  PAGE NUMBER	:
;		(AL)= 00H	WRITE CHARACTER STRING	:
;			BL     -  ATTRIBUTE	:
;			STRING IS  <CHAR,CHAR, ... ,CHAR>	:
;			CURSOR NOT MOVED	:
;		(AL)= 01H	WRITE CHARACTER STRING AND MOVE CURSOR	:
;			BL     -  ATTRIBUTE	:
;			STRING IS  <CHAR,CHAR, ... ,CHAR>	:
;			CURSOR MOVED		:
;		(AL)= 02H	WRITE CHARACTER AND ATTRIBUTE STRING	:
;			       (VALID FOR ALPHA MODES ONLY)	:
;			STRING IS <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>	:
;			CURSOR IS NOT MOVED	:
;		(AL)= 03H	WRITE CHARACTER AND ATTRIBUTE STRING AND MOVE CURSOR	:
;			       (VALID FOR ALPHA MODES ONLY)	:
;			STRING IS <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>	:
;			CURSOR IS MOVED	:
;		 NOTE:  CARRIAGE RETURN, LINE FEED, BACKSPACE, AND BELL ARE	:
;		        TREATED AS COMMANDS RATHER THAN PRINTABLE CHARACTERS.	:
;				;
;	BX,CX,DX,SI,DI,BP,SP,DS,ES,SS PRESERVED DURING CALLS EXCEPT FOR	:
;	BX,CX,DX RETURN VALUES ON FUNCTIONS 03H,04H,0DH AND 0FH. ON ALL CALLS	:
;	AX IS MODIFIED.		:
;--------------------------------------------------------------------------------

	ASSUME	CS:CODE,DS:DATA,ES:NOTHING

M1	DW	OFFSET	SET_MOOE	; TABLE OF ROUTINES WITHIN VIDEO I/O
	DW	OFFSET	SET_CTYPE
	DW	OFFSET	SET_CPOS
	DW	OFFSET	READ_CURSOR
	DW	OFFSET	READ_LPEN
	DW	OFFSET	ACT_DISP_PAGE
	DW	OFFSET	SCROLL_UP
	DW	OFFSET	SCROLL_DOWN
	DW	OFFSET	READ_AC_CURRENT
	DW	OFFSET	WRITE_AC_CURRENT
	DW	OFFSET	WRITE_C_CURRENT
	DW	OFFSET	SET_COLOR
	DW	OFFSET	WRITE_DOT
	DW	OFFSET	READ_DOT
	DW	OFFSET	WRITE_TTY
	DW	OFFSET	VIDEO_STATE
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	WRITE_STRING	; CASE 19H, WRITE STRING
M1L	EQU	$-M1

VIDEO_IO_1	PROC	NEAR	;	ENTRY POINT FOR ORG 0F065H
	STI		; INTERRUPTS BACK ON
	CLD		; SET DIRECTION FORWARD
	PUSH	ES
	PUSH	DS	; SAVE WORK AND PARAMETER REGISTERS
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	SI
	PUSH	DI
	PUSH	BP
	CALL	DDS	; POINT DS: TO DATA SEGMENT
	MOV	SI,0B800H	; GET SEGMENT FOR COLOR CARD
	MOV	DI,@EQUIP_FLAG	; GET EQUIPMENT FLAGS SETTING
	AND	DI,30H	; ISOLATE CRT SWITCHES
	CMP	DI,30H	; IS SETTING FOR BW CARD?
	JNE	M2	; SKIP IF NOT BW CARD
	MOV	SI,0B000H	; ELSE GET SEGMENT FOR BW CARD
M2:
	CMP	AH,13H	; TEST FOR WRITE STRING OPERATION
	JE	M3	; SKIP IF ES:BP VALID AS PASSED
	MOV	ES,SI	; SET UP TO POINT AT VIDEO MEMORY AREAS
M3:
	MOV	SI,AX	; MOVE COMMAND TO LOOK UP REGISTER
	SHR	SI,8	; SHIFT COMMAND TO FORM BYTE OFFSET
	SAL	SI,1	; TIMES 2 FOR WORD TABLE LOOKUP
	CMP	SI,M1L	; TEST FOR WITHIN TABLE RANGE
	JNB	M4	; BRANCH TO EXIT IFNOT A VALID COMMAND

	MOV	AH,@CRT_MODE	; MOVE CURRENT MODE INTO AH
	JMP	WORD PTR CS:[SI+OFFSET M1]	; GO TO SELECTED FUNCTION

M4:			; COMMAND NOT VALID
	JMP	VIDEO_RETURN	; DO NOTHING IF NOT IN VALID RANGE
VIDEO_IO_1	ENDP

;------------------------------------------------------
; SET MODE			:
;	THIS ROUTINE INITIALIZES THE ATTACHMENT TO	:
;	THE SELECTED MODE, THE SCREEN IS BLANKED.	:
; INPUT				:
;	(AL) - MODE SELECTED (RANGE 0-7)		:
; OUTPUT				:
;	NONE			:
;------------------------------------------------------
SET_MODE		PROC 	NEAR
	MOV	DX,03D4H	; ADDRESS OF COLOR CARD
	CMP	DI,30H	; IS BW CARD INSTALLED
	JNE	M8	; OK WITH COLOR
	MOV	AL,7	; INDICATE INTERNAL BW CARD MODE
	MOV	DL,0B4H	; ADDRESS OF BW (MONOCHROME) CARD
M8:
	MOV	@CRT_MODE,AL	; SAVE MODE IN GLOBAL VARIABLE
	MOV	@ADDR_6845,DX	; SAVE ADDRESS OF BASE
	MOV	@ROWS,25-1	; INITIALIZE DEFAULT ROW COUNT OF 25
	PUSH	DS	; SAVE POINTER TO DATA SEGMENT
	PUSH	AX	; SAVE MODE NUMBER (AL)
	CBW		; CLEAR HIGH BYTE OF MODE
	MOV	SI,AX	; SET TABLE POINTER, INDEXED BY MODE
	MOV	AL,CS:[SI + OFFSET M7]	; GET THE MODE SET VALUE FROM TABLE
	MOV	@CRT_MODE_SET,AL	; SAVE THE MODE SET VALUE
	AND	AL,037H	; VIDEO OFF, SAVE HIGH RESOLUTION SIT
	PUSH	DX	; SAVE OUTPUT PORT VALUE
	ADD	DX,4	; POINT TO CONTROL REGISTER
	OUT	DX,AL	; RESET VIDEO TO OFF TO SUPPRESS ROLLING
	POP	DX	; BACK TO BASE REGISTER
	ASSUME	DS:ABS0
	SUB	BX,BX	; SET UP FOR ABS0 SECMENT
	MOV	DS,BX	; ESTABLISH VECTOR TABLE ADDRESSING
	LDS	BX,@PARM_PTR	; GET POINTER TO VIDEO PARMS
	ASSUME 	DS:CODE
	POP	AX	; RECOVER MODE NUMBER IN (AL)
	MOV	CX,16	; LENGTH OF EACH ROW OF TABLE
	CMP	AL,2	; DETERMINE WHICH ONE TO USE
	JC	M9	; MODE IS 0 OR 1
	ADD	BX,CX	; NEXT ROW OF INITIALIZATION TABLE
	CMP	AL,4
	JC	M9	; MODE IS 2 OR 3
	ADD	BX,CX	; MOVE TO GRAPHICS ROW OF INIT_TABLE
	CMP	AL,7
	JC	M9	; MODE IS 4,5, OR 6
	ADD	BX,CX	; MOVE TO BW CARD ROW OF INIT_TABLE

;-----	BX POINTS TO CORRECT ROW OF INITIALIZATION TABLE

M9:			; OUT_INIT
	PUSH	AX	; SAVE MODE IN (AL)
	MOV	AX,[BX+10]	; GET THE CURSOR MODE FROM THE TABLE
	XCHG	AH,AL	; PUT CURSOR MODE IN CORRECT POSITION
	PUSH	DS	; SAVE TABLE SEGMENT POINTER
	ASSUME	DS:DATA
	CALL	DDS	; POINT DS TO DATA SEGMENT
	MOV	@CURSOR_MODE,AX	; PLACE INTO BIOS DATA SAVE AREA
	ASSUME	DS:CODE
	POP	DS	; RESTORE THE TABLE SEGMENT POINTER
	XOR	AH,AH	; AH IS REGISTER NUMBER DURING LOOP

;-----	LOOP THROUGH TABLE, OUTPUTTING REGISTER ADDRESS, THEN VALUE FROM TABLE

M10:			;  INITIALIZATION LOOP
	MOV	AL,AH	; GET 6845 REGISTER NUMBER
	OUT	DX,AL
	INC	DX	; POINT TO DATA PORT
	INC	AH	; NEXT REGISTER VALUE
	MOV	AL,[BX]	; GET TABLE VALUE
	OUT	DX,AL	; OUT TO CHIP
	INC	BX	; NEXT IN TABLE
	DEC	DX	; BACK TO POINTER REGISTER
	LOOP	M10	; DO THE WHOLE TABLE
	POP	AX	; GET MODE BACK INTO (AL)
	POP	DS	; RECOVER SEGMENT VALUE
	ASSUME	DS:DATA

;-----	FILL REGEN AREA WITH BLANK

	XOR	DI,DI	; SET UP POINTER FOR REGEN
	MOV	@CRT_START,DI	; START ADDRESS SAVED IN GLOBAL
	MOV	@ACTTVE_PAGE,0	; SET PAGE VALUE
	MOV	CX,8192	; NUMBER OF WORDS IN COLOR CARD
	CMP	AL,4	; TEST FOR GRAPHICS
	JC	M12	; NO_GRAPHICS_INIT
	CMP	AL,7	; TEST FOR BW CARD
	JE	M11	; BW_CARD_INIT
	XOR	AX,AX	; FILL FOR GRAPHICS MODE
	JMP	SHORT M13	; CLEAR BUFFER
M11:			; BW_CARD_INIT
	MOV	CH,08H	; BUFFER SIZE ON BW CARD (2048)
M12:			; NO_GRAPHICS_INIT
	MOV	AX,' '+7*H	; FIL  CHAR FOR ALPHA - ATTRIBUTE
M13:			; CLEAR BUFFER
	REP	STOSW	; FILL THE REGEN BUFFER WITH BLANKS

;-----	ENABLE VIDEO AND CORRECT PORT SETTING

	MOV	DX,@ADDR_6845	; PREPARE TO OUTPUT TO VIDEO ENABLE PORT
	ADD	DX,4	; POINT TO THE MODE CONTROL REGISTER
	MOV	AL,@CRT_MODE_SET	; GET THE MODE SET VALUE
	OUT	DX,AL	; SET VIDEO ENABLE PORT

;----- 	DETERMINE NUMBER OF COLUMNS, BOTH FOR ENTIRE DISPLAY
;----- 	AND THE NUMBER TO BE USED FOR TTY INTERFACE

	MOV	AL,CS:[SI + OFFSET M6]	; GET NUMBER OF COLUMNS ON THIS SCREEN
	CBW		; CLEAR HIGH BYTE
	MOV	@CRT_COLS,AX	; INITIALIZE NUMBER OF COLUMNS COUNT

;-----	SET CURSOR POSITIONS

	AND	SI,000EH	; WORD OFFSET INTO CLEAR LENGTH TABLE
	MOV	AX,CS:[SI + OFFSET M5]	; LENGTH TO CLEAR
	MOV	@CRT_LEN,AX	; SAVE LENGTH OF CRT -- NOT USED FOR SW
	MOV	CX,8	; CLEAR ALL CURSOR POSITIONS
	MOV	DI,OFFSET @CURSOR_POSN
	PUSH	DS	; ESTABLISH SEGMENT
	POP	ES	; ADDRESSING
	XOR	AX,AX
	REP	STOSW	; FILL WITH ZEROES

;-----	SET UP OVERSCAN REGISTER

	INC	DX	; SET OVERSCAN PORT TO A DEFAULT
	MOV	AL,30H	; 30H VALUE FOR ALL MODES EXCEPT 640X200
	CMP	@CRT_MODE,6	; SEE IF THE MODE IS 640X200 BW
	JNZ	M14	; IF NOT 640X200, THEN GO TO REGULAR
	MOV	AL,3FH	; IF IT IS 640X200, THEN PUT IN 3FM
M14:
	OUT	DX,AL	; OUTPUT THE CORRECT VALUE TO 3D9 PORT
	MOV	@CRT_PALETTE,AL	; SAVE THE VALUE FOR FUTURE USE

;-----	NORMAL RETURN FROM ALL VIDEO RETURNS

VIDEO_RETURN:
	POP	BP
	POP	DI
	POP	SI
	POP	BX
M15:			;VIDEO_RETURN_C
	POP	CX
	POP	DX
	POP	DS
	POP	ES	; RECOVER SEGMENTS
	IRET		; ALL DONE
SET_MODE		ENDP

;------------------------------------------------
; SET_CTYPE
;	THIS ROUTINE SETS THE CURSOR VALUE
; INPUT
;	(CX) HAS CURSOR VALUE CH-START LINE, CL-STOP LINE
; OUTPUT	
;	NONE
;------------------------------------------------
SET_CTYPE	PROC	NEAR
	MOV	AH,10	; 6845 REGISTER FOR CURSOR SET
	MOV	@CURSOR_MODE,CX	; SAVE IN DATA AREA
	CALL	M16	; OUTPUT CX REGISTER
	JMP	VIDEO_RETURN

;-----	THIS ROUTINE OUTPUTS THE CX REGISTER TO THE 6845 REGISTERS NAMED IN (AH)

M16:
	MOV	DX,@ADDR_6845	; ADDRESS REGISTER
	MOV	AL,AH	; GET VALUE
	OUT	DX,AL	; REGISTER SET
	INC	DX	; DATA REGISTER
	JMP	$+2	; I/O DELAY
	MOV	AL,CH	; DATA
	OUT	DX,AL
	DEC	DX
	MOV	AL,AH
	INC	AL	; POINT TO OTHER DATA REGISTER
	OUT	DX,AL	; SET FOR SECOND REGISTER
	INC	DX
	JMP	$+2	; I/O DELAY
	MOV	AL,CL	; SECOND DATA VALUE
	OUT	DX,AL
	RET		; ALL DONE
SET_CTYPE	ENDP

;----------------------------------------------
; SET_CPOS
;	THIS ROUTINE SETS THE CURRENT CURSOR POSITION TO THE
;	NEW X-Y VALUES PASSED
; INPUT
;	DX - ROW,COLUMN OF NEW CURSOR
;	BH - DISPLAY PAGE OF CURSOR
; OUTPUT
;	CURSOR ID SET AT 6845 IF DISPLAY PAGE IS CURRENT DISPLAY
;----------------------------------------------
SET_CPOS		PROC	NEAR
	MOV	AL,BH	; MOVE PAGE NUMBER TO WORK REGISTER
	CBW		; CONVERT PAGE TO WORD VALUE
	SAL	AX,1	; WORD OFFSET
	XCHG	AX,SI	; USE INDEX REGISTER
	MOV	[SI+OFFSET @CURSOR_POSN],DX	; SAVE THE POINTER
	CMP	@ACTIVE_PAGE,BH
	JNZ	M17	; SET_CPOS_RETURN
	MOV	AX,DX	; GET ROW/Z~QLUMN TO AX
	CALL	M18	; CURSOR SET
M17:			; SET_CPOS_RETURN
	JMP	VIDEO_RETURN
SET_CPOS		ENDP

;-----	SET CURSOR POSITION, AX HAS ROW/COLUMN FOR CURSOR

M18	PROC	NEAR
	CALL	POSITION	; DETERMINE LOCATION IN REGEN BUFFER
	MOV	CX,AX
	ADD	CX,@CRT_START	; ADD IN THE START ADDRESS FOR THIS PAGE
	SAR	CX,1	; DIVIDE BY 2 FOR CHAR ONLY COUNT
	MOV	AH,14	; REGISTER NUMBER FOR CURSOR
	CALL	M16	; OUTPUT THE VALUE TO THE 6845
	RET
M18 	ENDP
;------------------------------------------------------
; READ_CURSOR
;	THIS ROUTINE READS THE CURRENT CURSOR VALUE FROM THE
;	6845, FORMATS IT, AND SENDS IT BACK TO THE CALLER
; INPUT
;	BH - PAGE OF CURSOR
; OUTPUT
;	DX - ROW, COLUMN OF THE CURRENT CURSOR POSITION
;	CX - CURRENT CURSOR MODE
;------------------------------------------------------
READ-CURSOR	PROC	NEAR
	MOV	BL,BH
	XOR	BH,BH
	SAL	BX,1	; WORD OFFSET
	MOV	DX,[BX+OFFSET @CURSOR_POSN]
	MOV	CX,@CURSOR_MODE
	POP	BP
	POP	DI
	POP	SI
	POP	BX
	POP	AX	; DISCARD SAVED CX AND DX
	POP	AX
	POP	DS
	POP	ES
	IRET
READ_CURSOR	ENDP
;-----------------------------------------------------
; ACT_DISP_PAGE
;	THIS ROUTINE SETS THE ACTIVE DISPLAY PAGE, ALLOWING
;	THE FULL USE OF THE MEMORY SET ASIDE FOR THE VIDEO ATTACHMENT
; INPUT
;	AL HAS THE NEW ACTIVE DISPLAY PAGE
; OUTPUT
;	THE 6845 IS RESET TO DISPLAY THAT PAGE
;-----------------------------------------------------
ACT_DISP_PAGE	PROC 	NEAR
	MOV	@ACTIVE_PAGE,AL	; SAVE ACTIVE PAGE VALUE
	MOV	CX,@CRT_LEN	; GET SAVED LENGTH OF REGEN BUFFER
	CBW		; CONVERT AL TO WORD
	PUSH	AX	; SAVE PAGE VALUE
	MUL	CX	; DISPLAY PAGE TIMES REGEN LENGTH
	MOV	@CRT_START,AX	; SAVE START ADDRESS FOR LATER
	MOV	CX,AX	; START ADDRESS TO CX
	SAR	CX,1	; DIVIDE BY 2 FOR 6845 HANDLING
	MOV	AH,12	; 6845 REGISTER FOR START ADDRESS
	CALL	M16
	POP	BX	; RECOVER PAGE VALUE
	SAL	BX,1	; *2 FOR WORD OFFSET
	MOV	AX,[BX + OFFSET @CURSOR_POSN)	; GET CURSOR FOR THIS PAGE
	CALL	M18	; SET THE CURSOR POSITION
	JMP	VIDEO_RETURN
ACT_DISP_PAGE	ENDP
;-----------------------------------------------
; SET_COLOR
;	THIS ROUTINE SILL ESTABLISH THE BACKGROUND COLOR, THE OVERSCAN COLOR,
;	AND THE FOREGROUND COLOR SET FOR MEDIUM RESOLUTION GRAPHICS
; INPUT
;	(BH) HAS COLOR ID
;		IF BH=0, THE BACKGROUND COLOR VALUE IS SET
;		        FROM THE LOW BITS OF BIT (0-31)
;		IF BH=1, THE PALETTE SELECTION IS MADE
;		        BASED ON THE LOW BIT OF BL:
;		                0 = GREEN, RED,	YELLOW FOR COLORS 1,2,3
;		                1 = BLUE, CYAN, MAGENTA FOR COLORS 1,2,3
;	(BL) HAS THE COLOR VALUE TO BE USED
; OUTPUT
;	THE COLOR SELECTION IS UPDATED
;------------------------------------------------
SET_COLOR	PROC	NEAR
	MOV	DX,@ADDR_6845	; I/O PORT FOR PALETTE
	ADD	DX,5	; OVERSCAN PORT
	MOV	AL,@CRT_PALETTE	; GET THE CURRENT PALETTE VALUE
	OR	BH,BH	; IS THIS COLOR 0?
	JNZ	M20	; OUTPUT COLOR 1

;-----	HANDLE COLOR 0 BY SETTING THE BACKGROUND COLOR

	AND	AL,0E0H	; TURN OFF LOW 5 BITS OF CURRENT
	AND	BL,01FH	; TURN OFF HIGH 3 BITS OF INPUT VALUE
	OR	AL,BL	; PUT VALUE INTO REGISTER
M19:			; OUTPUT THE PALETTE
	OUT	DX,AL	; OUTPUT COLOR SELECTION TO 3D9 PORT
	MOV	@CRT_PALETTE,AL	; SAVE THE COLOR VALUE
	JMP	VIDEO_RETURN

;-----	HANDLE COLOR 1 BY SELECTING THE PALETTE TO BE USED

M20:
	AND	AL,0DFH	; TURN OFF PALETTE SELECT BIT
	SHR	BL,1	; TEST THE LOW ORDER BIT OF SL
	JNC	M19	; ALREADY DONE
	OR	AL,20H	; TURN ON PALETTE SELECT BIT
	JMP	M19	; GO DO IT
SET_COLOR	ENDP
;---------------------------------------------------
; VIDEO STATE
;  RETURNS THE CURRENT VIDEO STATE IN AX
;  AH = NUMBER OF COLUMNS ON THE SCREEN
;  AL = CURRENT VIDEO MODE
;  BH = CURRENT ACTIVE PAGE
;---------------------------------------------------
VIDEO_STATE	PROC	NEAR
	MOV	AH,BYTE PTR @CRT_COLS	; GET NUMBER OF COLUMNS
	MOV	AL,@CRT_MODE	; CURRENT MODE
	MOV	BH,@ACTTVE_PAGE	; GET CURRENT ACTIVE PAGE
	POP	BP	; RECOVER REGISTERS
	POP	DI
	POP	SI
	POP	CX	; DISCARD SAVED BX
	JMP	M15	; RETURN TO CALLER
VIDEO-STATE	ENDP
;-----------------------------------------
; POSITION
;	THIS SERVICE ROUTINE CALCULATES THE REGEN BUFFER ADDRESS
;	OF A CHARACTER IN THE ALPHA MODE
; INPUT
;	AX = ROW, COLUMN POSITION
; OUTPUT
;	AX = OFFSET OF CHAR POSITION IN REGEN BUFFER
;-----------------------------------------
POSITION		PROC	NEAR
	PUSH	BX	; SAVE REGISTER
	MOV	BX,AX
	MOV	AL,AH	; ROWS TO AL
	MUL	BYTE PTR @CRT_COLS	; DETERMINE BYTES TO ROW
	XOR	BH,BH
	ADD	AX,BX	; ADD IN COLUMN VALUE
	SAL	AX,1	; * 2 FOR ATTRIBUTE BYTES
	POP	BX
	RET
POSITION		ENDP
;----------------------------------------------
; SCROLL UP
;	THIS ROUTINE MOVES A BLOCK OF CHARACTERS UP
;	ON THE SCREEN
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF ROWS TO SCROLL
;	(CX) = ROW/COLUMN OF UPPER LEFT CORNER
;	(DX) = ROW/COLUMN OF LOWER RIGHT CORNER
;	(BH) = ATTRIBUTE TO BE USED ON BLANKED LINE
;	(DS) = DATA SEGMENT
;	(ES) = REGEN BUFFER SEGMENT
; OUTPUT
;	NONE -- THE REGEN BUFFER IS MODIFIED
;--------------------------------------------
	ASSUME 	DS:DATA,ES:DATA
SCROLL_UP	PROC	NEAR
	CALL	TEST_LINE_COUNT
	CMP	AH,4	; TEST FOR GRAPHICS MODE
	JC	N1	; HANDLE SEPARATELY
	CMP	AH,7	; TEST FOR BW CARD
	JE	N1
	JMP	GRAPHICS_UP
N1:			; UP_CONTINUE
	PUSH	BX	; SAVE FILL ATTRIBUTE IN BH
	MOV	AX,CX	; UPPER LEFT POSITION
	CALL	SCROLL_POSITION	; DO SETUP FOR SCROLL
	JZ	N7	; BLANK_FIELD
	ADD	SI,AX	; FROM ADDRESS
	MOV	AH,DH	; # ROWS IN BLOCK
	SUB	AH,BL	; # ROWS TO BE MOVED
N2:			; ROW LOOP
	CALL	N10	; MOVE ONE ROW
	ADD	SI,BP
	ADD	DI,BP	; POINT TO NEXT LINE IN BLOCK
	DEC	AH	; COUNT OF LINES TO MOVE
	JNZ	N2	; ROW_LOOP
N3:			; CLEAR ENTRY
	POP	AX	; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '	; FILL WITH BLANKS
N4:			; CLEAR_LOOP
	CALL	N11	; CLEAR THE ROW
	ADD	DI,BP	; POINT TO NEXT LINE
	DEC	BL	; COUNTER OF LINES TO SCROLL
	JNZ	N4	; CLEAR_LOOP
N5:			; SCROLL_END
	CALL	DDS
	CMP	@CRT_MODE,7	; IS THIS THE BLACK AND WHITE CARD
	JE	N6	; IF SO, SKIP THE MODE RESET
	MOV	AL,@CRT_MODE_SET	; GET THE VALUE OF THE MODE SET
	MOV	DX,03D8H	; ALWAYS SET COLOR CARD PORT
	OUT	DX,AL
N6:			; VIDEO_RET_HERE
	JMP	VIDEO_RETURN
N7:			; BLANK_FIELD
	MOV	BL,DH	; GET ROW COUNT
	JMP	N3	; GO CLEAR THAT AREA
SCROLL_UP	ENDP

I ----- HANDLE COMMON SCROLL SET UP HERE

SCROLL_POSITION	PROC 	NEAR
	CALL	POSITION	; CONVERT TO REGEN POINTER
	ADD	AX,@CRT_START	; OFFSET OF ACTIVE PAGE
	MOV	DI,AX	; TO ADDRESS FOR SCROLL
	MOV	SI,AX	; FROM ADDRESS FOR SCROLL
	SUB	DX,CX	; DX = #ROWS, #COLS IN BLOCK
	INC	DH
	INC	DL	; INCREMENT FOR 0 ORIGIN
	XOR	CH,CH	; SET HIGH BYTE OF COUNT TO ZERO
	MOV	BP,@CRT_COLS	; GET NUMBER OF COLUMNS IN DISPLAY
	ADD	BP,BP	; TIMES 2 FOR ATTRIBUTE BYTE
	MOV	AL,BL	; GET LINE COUNT
	MUL	BYTE PTR @CRT_COLS	; DETERMINE OFFSET TO FROM ADDRESS
	ADD	AX,AX	; *2 FOR ATTRIBUTE BYTE
	PUSH	AX	; SAVE LINE COUNT
	MOV	AL,@CRT_MODE	; GET CURRENT MODE
	PUSH	ES	; ESTABLISH ADDRESSING TO REGEN BUFFER
	POP	DS	; FOR BOTH POINTERS
	CMP	AL,2	; TEST FOR COLOR CARD SPECIAL CASES HERE
	JB	N9	; HAVE TO HANDLE 80X25 SEPARATELY
	CMP	AL,3
	JA	N9
;-----			; 80X25 COLOR CARD SCROLL
	PUSH	DX
	MOV	DX,3DAH	; GUARANTEED TO BE COLOR CARD HERE
N8:			; WAIT_DISP_ENABLE
	IN	AL,DX	; GET PORT
	TEST	AL,RVRT	; WAIT FOR VERTICAL RETRACE
	JZ	N8	; WAIT_DISP_ENABLE
	MOV	AL,25H
	MOV	DL,0D8H	; ADDRESS CONTROL PORT
	OUT	DX,AL	; TURN OFF VIDEO DURING VERTICAL RETRACE
	POP	DX
N9:
	POP	AX	; RESTORE LINE COUNT
	OR	BL,BL	; 0 SCROLL MEANS BLANK FIELD
	RET		; RETURN WITH FLAGS SET
SCROLL_POSITION	ENDP

;-----	MOVE_ROW
N10	PROC	NEAR
	MOV	CL,DL	; GET # OF COLS TO MOVE
	PUSH	SI
	PUSH	DI	; SAVE START ADDRESS
	REP	MOVSW	; MOVE THAT LINE ON SCREEN
	POP	DI
	POP	SI	; RECOVER ADDRESSES
	RET
N10	ENDP

;-----	CLEAR_ROW
N11	PROC	NEAR
	MOV	CL,DL	; GET # COLUMNS TO CLEAR
	PUSH	DI
	REP	STOSW	; STORE THE FILL CHARACTER
	POP	DI
	RET
N11	ENDP
;------------------------------------------
; SCROLL DOWN
;	THIS ROUTINE MOVES THE CHARACTERS WITHIN A DEFINED
;	BLOCK DOWN ON THE SCREEN, FILLING THE TOP LINES
;	WITH A DEFINED CHARACTER
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF LINES TO SCROLL
;	(CX) = UPPER LEFT CORNER OF RECION
;	(DX) = LOWER RIGHT CORNER OF REGION
;	(BH) = FILL CHARACTER
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	NONE -- SCREEN IS SCROLLED
;------------------------------------------
SCROLL_DOWN	PROC	NEAR
	STD		; DIRECTION FOR SCROLL DOWN
	CALL	TEST_LINE_COUNT
	CMP	AH,4	; TEST FOR GRAPHICS
	JC	N12
	CMP	AH,7	; TEST FOR BW CARD
	JE	N12
	JMP	GRAPHICS_DOWN
N12:			; CONTINUE_DOWN
	PUSH	BX	; SAVE ATTRIBUTE IN BH
	MOV	AX,DX	; LOWER RIGHT CORNER
	CALL	SCROLL_POSITION	; GET REGEN LOCATION
	JZ	N16
	SUB	SI,AX	; SI IS FROM ADDRESS
	MOV	AH,DH	; GET TOTAL # ROWS
	SUB	AH,BL	; COUNT TO MOVE IN SCROLL
N13:
	CALL	N10	; MOVE ONE ROW
	SUB	SI,BP
	SUB	DI,BP
	DEC	AH
	JNZ	N13
N14:
	POP	AX	; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '
N15:
	CALL	N11	; CLEAR ONE ROW
	SUB	DL,BP	; GO TO NEXT ROW
	DEC	BL
	JNZ	N15
	JMP	N5	; SCROLL_END
N16:
	MOV	BL,DH
	JMP	N14
SCROLL_DOWN	ENDP

;-----	IF AMOUNT OF LINES TO BE SCROLLED = AMOUNT OF LINES IN WINDOW
;-----	THEN ADJUST AL; ELSE RETURN;

TEST_LINE_COUNT PROC 	NEAR

	MOV	BL,AL	; SAVE LINE COUNT IN BL
	OR	AL,AL	; TEST IF AL IS ALREADY ZERO
	JZ	BL_SET	; IF IT IS THEN RETURN 
	PUSH	AX	; SAVE AX
	MOV	AL,DH	; SUBTRACT LOWER ROW FROM UPPER ROW
	SUB	AL,CH
	INC	AL	; ADJUST DIFFERENCE BY 1
	CMP	AL,BL	; LINE COUNT = AMOUNT OF ROWS IN WINDOW?
	POP	AX	; RESTORE AX
	JNE	BL_SET	; IF NOT THEN WE'RE ALL SET
	SUB	BL,BL	; OTHERWISE SET BL TO ZERO
BL_SET:
	RET		; RETURN
TEST_LINE_COUNT ENDP

;------------------------------------------------------------------------------
; READ_AC_CURRENT		:
;	THIS ROUTINE READS THE ATTRIBUTE AND CHARACTER AT THE CURRENT	:
;	CURSOR POSITION AND RETURNS THEM TO THE CALLER	:
; INPUT				:
;	(AH) = CURRENT CRT MODE		:
;	(BH) = DISPLAY PAGE ( ALPHA MODES ONLY )	:
;	(DS) = DATA SEGMENT		:
;	(ES) = REGEN SEGMENT		:
; OUTPUT				:
;	(AL) = CHARACTER READ		:
;	(AH) = ATTRIBUTE READ		:
;------------------------------------------------------------------------------
	ASSUME	DS:DATA,ES:DATA

READ_AC_CURRENT PROC 	NEAR
	CMP	AH,4	; IS THIS GRAPHICS
	JC	P10

	CMP	AH,7	; IS THIS BW CARD
	JE	P10

	JMP	GRAPHICS_READ	;  READ_AC_CONTINUE
P10:
	CALL	FIND_POSITION	; GET REGEN LOCATION AND PORT ADDRESS
	MOV	SI,DI	; ESTABLISH ADDRESSING IN SI
	PUSH	ES	; GET REGEN SEGMENT FOR QUICK ACCESS
	POP	DS

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

	OR	BL,BL	; CHECK MODE FLAG FOR COLOR CARD IN 80
	JNZ	P13	; ELSE SKIP RE-RACE WAIT - DO FAST READ
P11:			;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI		; ENABLE INTERRUPTS FIRST
	NOR		; ALLOW FOR SMALL INTERRUPT WINDOW
	CLI		; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX	; GET STATUS FROM THE ADAPTER
	TEST	AL,RHRZ	; IS HORIZONTAL RETRACE LOW
	JNZ	P11	; WAIT UNTIL IT IS
P12:			;  NOW WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX	; GET STATUS
	TEST	AL,RVRT+RHRZ	; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P12	; WAIT UNTIL EITHER IS ACTIVE
P13:
	LODSW		; GET THE CHARACTER AND ATTRIBUTE
	JMP	VIDEO_RETURN	; EXIT WITH (AX)

READ_AC_CURRENT	ENDP

FIND_POSITTON	PROC	NEAR	;	  SETUP FOR BUFFER READ OR WRITE
	XCHG	AH,BL	; SWAP MODE TYPE WITH ATTRIBUTE
	MOV	BP,AX	; SAVE CHARACTER/ATTR IN (BP) REGISTER
	SUB	BL,2	; CONVERT DISPLAY MODE TYPE TO A
	SHR	BL,1	;  ZERO VALUE FOR COLOR IN 80 COLUMN
	MOV	SI,BX	;  AND SAVE (2 OR 3 --> ZERO)
	MOV	BL,BH	; MOVE DISPLAY PAGE TO LOW BYTE
	XOR	BH,BH	; CLEAR HIGH BYTE OF COUNT/BYTE OFFSET
	MOV	DI,BX	; MOVE DISPLAY PAGE (COUNT) TO WORK REG
	SAL	DI,1	; TIMES 2 FOR WORD OFFSET
	MOV	AX,[DI+OFFSET @CURSOR_POSN)	; GET ROW/COLUMN OF THAT PAGE
	JZ	P21	; SKIP BUFFER ADJUSTMENT IF PAGE ZERO

	XOR	DI,DI	; ELSE SET BUFFER START ADDRESS TO ZERO
P20:
	ADD	DI,@CRT_LEN	; ADD LENGTH OF BUFFER FOR ONE PAGE
	DEC	BX	; DECREMENT PAGE COUNT
	JNZ	P20	; LOOP TILL PAGE COUNT EXHAUSTED
P21:
	CALL	POSITION	; DETERMINE LOCATION IN REGEN IN PAGE
	ADD	DI,AX	; ADD LOCATION TO START OF REGEN PAGE
	MOV	DX,@ADDR_6845	; GET BASE ADDRESS OF ACTIVE DISPLAY
	ADD	DX,6	; POINT AT STATUS PORT
	MOV	AX,SI	; RECOVER CONVERTED MODE TYPE IN (BL)
	RET		;  BP= ATTRIBUTE/CHARACTER (FROM BL/AL)
			;  DI= POSITION (OFFSET IN REGEN BUFFER)
			;  DX= STATUS PORT ADDRESS OF ADAPTER
FIND_POSITION	ENDP	;  BL= MODE FLAG (ZERO FOR 80X25 COLOR)

;------------------------------------------------------------------------------
; WRITE_AC_CURRENT		:
;	THTS ROUTINE WRITES THE ATTRIBUTE AND CHARACTER	:
;	AT THE CURRENT CURSOR POSITION		:
; INPUT				:
;	(AH) = CURRENT CRT MODE		:
;	(BH) = DISPLAY PAGE		:
;	(CX) = COUNT OF CHARACTERS TO WRITE	:
;	(AL) = CHAR TO WRITE		:
;	(BL) = ATTRIBUTE OF CHAR TO WRITE	:
;	(DS) = DATA SEGMENT		:
;	(ES) = REGEN SEGMENT		:
; OUTPUT				:
;	DISPLAY REGEN BUFFER UPDATED		:
;------------------------------------------------------------------------------
WRITE_AC_CURRENT		PROC	NEAR
	CMP	AH,4	; IS THIS GRAPHICS
	JC	P30
	CMP	AH,7	; IS THIS BW CARD
	JE	P30
	JMP	GRAPHICS_WRITE
P30:			; WRITE_AC_CONTINUE
	CALL	FIND_POSITION	; GET REGEN LOCATION AND PORT ADDRESS
			; ADDRESS IN (DI) REGISTER
	OR	BL,BL	; CHECK MODE FLAG FOR COLOR CARD AT 80
	JZ	P32	; SKIP TO RETRACE WAIT IF COLOR AT 80

	XCHG	AX,BP	; GET THE ATTR/CHAR SAVED FOR FAST WRITE
	REP	STOSW	; STRING WRITE THE ATTRIBUTE A CHARACTER
	JMP	SHORT P35	; EXIT FAST WRITE ROUTINE

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

P31:			;  LOOP FOR EACH ATTR/CHAR WRITE
	XCHG	BP,AX	; PLACE ATTR/CHAR BACK IN SAVE REGISTER
P32:			;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI		; ENABLE INTERRUPTS FIRST
	NOP		; ALLOW FOR INTERRUPT WINDOW
	CLI		; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX	; GET STATUS FROM THE ADAPTER
	TEST	AL,RVRT	; CHECK FOR VERTICAL RETRACE FIRST
	JNZ	P34	; DO FAST WRITE NOW IF VERTICAL RETRACE
	TEST	AL,RHRZ	; HORIZONTAL RETRACE LOW THEN
	JNZ	P32	; WAIT UNTIL IT IS
P33:			; WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX	; GET STATUS AGAIN
	TEST	AL,RVRT+RHRZ	; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P33	; WAIT UNTIL EITHER IS ACTIVE
P34:
	XCHG	AX,BP	; GET THE ATTR/CHAR SAVED IN (BP)
	STOSW		; WRITE THE ATTRIBUTE AND CHARACTER
	LOOP	P31	; AS MANU TIMES AS REQUESTED - TILL CX=0
P35:
	JMP	VIDEO_RETURN	; EXIT

WRITE_AC_CURRENT		ENDP

;------------------------------------------------------------------------------
; WRITE_C_CURRENT		:
;	THIS ROUTINE WRITES THE CHARACTER AT	:
;	THE CURRENT CURSOR POSITION, ATTRIBUTE UNCHANGED	:
; INPUT				:
;	(AH) = CURRENT CRT MODE		:
;	(BH) = DISPLAY PAGE		:
;	(CX) = COUNT OF CHARACTERS TO WRITE	:
;	(AL) = CHAR TO WRITE		:
;	(DS) = DATA SEGMENT		:
;	(ES) = REGEN SEGMENT		:
; OUTPUT				:
;	DISPLAY REGEN BUFFER UPDATED		:
;------------------------------------------------------------------------------
WRITE_C_CURRENT	PROC 	NEAR
	CMP	AH,4	; IS THIS GRAPHICS
	JC	P40
	CMP	AH,7	; IS THIS BW CARD
	JE	P40
	JMP	GRAPHICS_WRITE
P40:
	CALL	FIND_POSITION	; GET REGEN LOCATION AND PORT ADDRESS
			; ADDRESS OF LOCATION IN (DI)

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

P41:			;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI		; ENABLE INTERRUPTS FIRST
	OR	BL,BL	; CHECK MODE FLAG FOR COLOR CARD IN 80
	JNZ	P43	; ELSE SKIP RETRACE WAIT - DO FAST WRITE
	CLI		; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX	; GET STATUS FROM THE ADAPTER
	TEST	AL,RVRT	; CHECK FOR VERTICAL RETRACE FIRST
	JNZ	P43	; DO FAST WRITE NOW IF VERTICAL RETRACE
	TEST	AL,RHRZ	; IS HORIZONTAL RETRACE LOW THEN
	JNZ	P41	; WAIT UNTIL IT IS
P42:			;  WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX	; GET STATUS AGAIN
	TEST	AL,RVRT+RHRZ	; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P42	; WAIT UNTIL EITHER RETRACE ACTIVE
P43:
	MOV	AX,BP	; GET THE CHARACTER SAVE IN (BP)
	STOSB		; PUT THE CHARACTER INTO REGEN BUFFER
	INC	DI	; BUMP POINTER PAST ATTRIBUTE
	LOOP	P41	;  AS MANY TIMES AS REQUESTED
	JMP	VIDEO_RETURN
WRITE_C_CURRENT	ENDP

;------------------------------------------------------------------------------
; WRITE_STRING			:
;	THIS ROUTINE WRITES A STRING OF CHARACTERS TO THE CRT.	:
; INPUT				:
;	(AL) = WRITE STRING COMMAND 0 - 3	:
;	(BH) = DISPLAY PAGE		:
;	(CX) = COUNT OF CHARACTERS TO WRITE, IF (CX) = 0 THEN RETURN	:
;	(DX) = CURSOR POSITION FOR START OF STRING WRITE	:
;	(BL) = ATTRIBUTE OF CHARACTER TO WRITE IF (AL) = 0 OR (AL) = 1	:
;	(ES) = SOURCE STRING SEGMENT		:
;	(BP) = SOURCE STRING OFFSET		:
; OUTPUT				:
;	NONE			:
;------------------------------------------------------------------------------
WRITE_STRING	PROC	NEAR
	CMP	AL,04	; TEST FOR INVALID WRITE STRING OPTION
	JNB	P59	; IF OPTION INVALID THEN RETURN
	JCXZ	P59	; IF ZERO LENGTH STRING THEN RETURN

	MOV	SI,BX	; GET CURRENT CURSOR PAGE
	SHR	SI,8	; CLEAR HIGH BYTE
	SAL	SI,1	; CONVERT TO PAGE OFFSET (SI= PAGE)
	PUSH	[SI+OFFSET @CURSOR_POSN]	; SAVE CURRENT CURSOR POSITION IN STACK
	PUSH	AX	; SAVE WRITE STRING OPTION
	MOV	AX,0200H	; SET NEW CURSOR POSITION
	INT	10H
	POP	AX	; RESTORE WRITE STRING OPTION
P50:
	PUSH	CX
	PUSH	BX
	PUSH	AX
	XCHG	AH,AL	; PUT THE WRITE STRING OPTION INTO 'AH)
	MOV	AL,ES:[BP]	; GET CHARACTER FROM INPUT STRING
	INC	BP	; BUMP POINTER TC CHARACTER

;-----	TEST FOR SPECIAL CHARACTER'S

	CMP	AL,08H	; IS IT A BACKSPACE
	JE	P51	; BACK SPACE
	CMP	AL,CR	; IS IT CARRIAGE RETURN
	JE	P51	; CAR_RET
	CMP	AL,LF	; IS IT A LINE FEED
	JE	P51	; LINE_FEED
	CMP	AL,07H	; IS IT A BELL
	JNE	P52	; IF NOT THEN DO WRITE CHARACTER
P51:
	MOV	AH,0EH	; TTY CHARACTER WRITE
	INT	10H	; WRITE TTY CHARACTER TO THE CRT
	MOV	DX,[SI+OFFSET @CURSOR_POSN]	; GET CURRENT CURSOR POSITION
	POP	AX	; RESTORE RECISTERS
	POP	BX
	POP	CX
	JMP	SHORT P54	; GO SET CURSOR POSITION AND CONTINUE
P52:
	MOV	CX,1	; SET CHARACTER WRITE AMOUNT TO ONE
	CMP	AH,2	; IS THE ATTRIBUTE IN THE STRING
	JB	P53	; IF NOT 7HEN SKIP
	MOV	BL,ES:[BP]	; ELSE GET NEW ATTRIBUTE
	INC	BP	; BUMP STRING POINTER
P53:
	MOV	AH,09H	;  GOT_CHARACTER
	INT	10H	; WRITE CHARACTER TO THE CRT
	POP	AX	; RESTORE REGISTERS
	POP	BX
	POP	CX
	INC	DL	; INCREMENT COLUMN COUNTER
	CMP	DL,BYTE PTR @CRT_COLS	; IF COLS ARE WITHIN RANGE FOR THIS MODE
	JB	P54	;   THEN GO TO COLUMNS SET
	INC	DH	; BUMP ROW COUNTER BY ONE
	SUB	DL,DL	; SET COLUMN COUNTER TO ZERO
	CMP	DH,25	; IF ROWS ARE LESS THAN 25 THEN
	JB	P54	;   GO TO ROWS_COLUMNS_SET

	PUSH	AX	; ELSE SCROLL SCREEN
	MOV	AX,OEOAH	; DO SCROLL ONE LINE
	INT	IOH	; RESET ROW COUNTER TO 24
	DEC	OH
	POP	AX	; RESTORE REGISTERS
P54:			;  ROW_COLUMNS_SET
	PUSH	AX	; SAVE WRITE STRING OPTION
	MOV	AX,0200H	; SET NEW CURSOR POSITION COMMAND
	INT	10H	; ESTABLISH NEW CURSOR POSITION
	POP	AX
	LOOP	P50	; DO IT ONCE MORE UNTIL (CX) = ZERO
	POP	DX	; RESTORE OLD CURSOR COORDINATES
	TEST	AL,01H	; IF CURSOR WAS NOT TO BE MOVED THEN
	JNZ	P59	;  THEN EXIT WITHOUT RESETTING OLD VALUE
	MOV	AX,0200H	; ELSE RESTORE OLD CURSOR POSITION
	INT	10H
P59:			; DONE - EXIT WRITE STRING
	JMP	VIDEO_RETURN	; RETURN TO CALLER

WRITE_STRING 	ENDP
;-----------------------------------------------
; READ DOT -- WRITE DOT
; THESE ROUTINES WILL WRITE A DOT, OR READ THE
;  DOT AT THE INDICATED LOCATION
; ENTRY --
;   DX = ROW (0-199)    (THE ACTUAL VALUE DEPENDS ON THE MODE)
;   CX = COLUMN ( 0-639)  (THE VALUES ARE NOT RANGE CHECKED )
;   AL = DOT VALUE TO WRITE (1,2 OR 4 BITS DEPENDING ON MODE,
;       REQUIRED FOR WRITE DOT ONLY, RIGHT JUSTIFIED)
;       BIT 7 OF AL = 1 INDICATES XOR THE VALUE INTO THE LOCATION
;   DS = DATA SEGMENT
;   ES = REGEN SEGMENT
;
; EXIT
;	AL = DOT VALUE READ, RIGHT JUSTIFIED, READ ONLY
;-----------------------------------------------
	ASSUME	DS:DATA,ES:DATA
READ_DOT		PROC	NEAR
	CALL	R3	; DETERMINE BYTE POSITION OF DOT
	MOV	AL,ES:[SI]	; GET THE BYTE
	AND	AL,AH	; MASK OFF THE OTHER BITS IN THE BYTE
	SHL	AL,CL	; LEFT JUSTIFY THE VALUE
	MOV	CL,DH	; GET NUMBER OF BITS IN RESULT
	ROL	AL,CL	; RIGHT JUSTIFY THE RESULT
	JMP	VIDEO_RETURN	; RETURN FROM VIDEO I/O
READ_DOT		ENDP

WRITE_DOT	PROC	NEAR
	PUSH	AX	; SAVE DOT VALUE
	PUSH	AX	;  TWICE
	CALL	R3	; DETERMINE BYTE POSITION OF THE DOT
	SHR	AL,CL	; SHIFT TO SET UP THE BITS FOR OUTPUT
	AND	AL,AH	; STRIP OFF THE OTHER BITS
	MOV	CL,ES:[SI]	; GET THE CURRENT BYTE
	POP	BX	; RECOVER XOR FLAG
	TEST	BL,80H	; IS IT ON
	JNZ	R2	; YES, XOR THE DOT
	NOT	AH	; SET MASK TO REMOVE THE INDICATED BITS
	AND	CL,AH
	OR	AL,CL	; OR IN THE NEW VALUE OF THOSE BITS
R1:			; FINISH DOT
	MOV	ES:[SI],AL	; RESTORE THE BYTE IN MEMORY
	POP	AX
	JMP	VIDEO_RETURN	; RETURN FROM VIDEO I/O
R2:			; XOR_DOT
	XOR	AL,CL	; EXCLUSIVE OR THE DOTS
	JMP	R1	; FINISH UP THE WRITING
WRITE_DOT	ENDP
;-----------------------------------------------
; THIS SUBROUTINE DETERMINES THE REGEN BYTE LOCATION OF THE
; INDICATED ROW COLUMN VALUE IN GRAPHICS MODE.
; ENTRY --
;  DX = ROW VALUE (0-199)
;  CX = COLUMN VALUE (0-639)
; EXIT --
;  SI = OFFSET INTO REGEN BUFFER FOR BYTE OF INTEREST
;  AH = MASK TO STRIP OFF THE BITS OF INTEREST
;  CL = BITS TO SHIFT TO RIGHT JUSTIFY THE MASK IN AH
;  DH = # BITS IN RESULT
;  BX = MODIFIED
;-----------------------------------------------
R3	PROC 	NEAR

;-----	DETERMINE 1ST BYTE IN INDICATED ROW BY MULTIPLYING ROW VALUE BY 40
;-----	LOW BIT OF ROW DETERMINES EVEN/ODD, 80 BYTES/ROW )

	XCHG	AX,BX	; WILL SAVE AL AND AH DURING OPERATION
	MOV	AL,40
	MUL	DL	; AX= ADDRESS OF START OF INDICATED ROW
	TEST	AL,008H	; TEST FOR EVEN/ODD ROW CALCULATED
	JZ	R4	; JUMP IF EVEN ROW
	ADD	AX,200OH-40	; OFFSET TO LOCATION OF ODD ROWS ADJUST
R4:			; EVEN ROW
	XCHG	SI,AX	; MOVE POINTER TO SI
	XCHG	AX,BX	; RECOVER AL AND AH VALUES
	MOV	DX,CX	; COLUMN VALUE TO DX

;-----	DETERMINE GRAPHICS MODE CURRENTLY IN EFFECT

; SET UP THE REGISTERS ACCORDING TO THE MODE
; CH = MASK FOR LOW OF COLUMN ADDRESS ( 7/3 FOR HIGH/MED RES )
; CL = # OF ADDRESS BITS IN COLUMN VALUE ( 3/2 FOR H/M )
; BL = MASK TO SELECT BITS FROM POINTED BYTE ( 80H/C0H FOR H/M )
; BH = NUMBER OF VALID BITS IN POINTED BYTE ( 1/2 FOR H/M )

	MOV	SX,2COM
	MOV	CX,302H	; SET PARMS FOR MED RES
	CMP	OCRT_MODE,B
	JC	R5	; HANDLE IF MED RES
	MOV	EX'130H
	MOV	CX,T03H	; SET PARMS FOR HIGH RES

;-----	DETERMINE BIT OFFSET IN BYTE FROM COLUMN MASK
R5:
	AND	CH,DL	; ADDRESS OF PEL WITHIN BYTE TO CH

;-----	DETERMINE BYTE OFFSET FOR THIS LOCATION IN COLUMN

	SHR	DX,CL	; SHIFT BY CORRECT AMOUNT
	ADD	SI,DX	; INCREMENT THE POINTER
	MOV	DH,BH	; GET THE # OF BITS IN RESULT TO CH

;-----	MULTIPLY BH (VALID BITS IN BYTE) BY CH (BIT OFFSET)

	SUB	CL,CL	; ZERO INTO STORAGE LOCATION
R6:
	ROR	AL,1	; LEFT JUSTIFY VALUE IN AL (FOR WRITE)
	ADD	CL,CH	; ADD IN THE BIT OFFSET VALUE
	DEC	BH	; LOOP CONTROL
	JNZ	R6	; ON EXIT, CL HAS COUNT TO RESTORE BITS
	MOV	AH,BL	;  GET MASK TO AH
	SHR	AH,CL	;  MOVE THE MASK TO CORRECT LOCATION
	RET		;  RETURN WITH EVERYTHING SET UP
R3 	ENDP
;-------------------------------------------------------
;  SCROLL UP
;   THIS ROUTINE SCROLLS UP THE INFORMATION ON THE CRT
; ENTRY ---
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;-------------------------------------------------------
GRAPHICS_UP	PROC	NEAR
	MOV	BL,AL	; SAVE LINE COUNT IN BL
	MOV	AX,CX	; GET UPPER LEFT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL 	GRAPH_POSN
	MOV	DI,AX	; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H	; ADJUST VALUES
	SAL	DH,2	; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
			;  AND EVEN/ODD ROWS
;-----	DETERMINE CRT MODE

	CMP	@CRT_MODE,6	; TEST FOR MEDIUM RES
	JNC	R7	; FIND_SOURCE

;-----	MEDIUM RES UP
	SAL	DL,1	; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI,1	; OFFSET *2 SINCE 2 BYTES/CHAR

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R7:			; FIND_SOURCE
	PUSH	ES	; GET SEGMENTS BOTH POINTING TO REGEN
	POP	05
	SUB	CH,CH	; ZERO TO HIGH OF COUNT REGISTER
	SAL	BL,2	; MULTIPLY NUMBER OF LINES BY 4
	JZ	R11	; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,BL	; GET NUMBER OF LINES IN AL
	MOV	AH,80	; 80 BYTES/ROW
	MUL	AH	; DETERMINE OFFSET TO SOURCE
	MOV	S1,01	; SET UP SOURCE
	ADD	SI,AX	;  ADD IN OFFSET TO IT
	MOV	AH,DH	; NUMBER OF ROWS IN FIELD
	SUB	AH,BL	; DETERMINE NUMBER TO MOVE

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R8:			; ROW_ LOOP
	CALL	R17	; MOVE ONE ROW
	SUB	SI,2000H-80	; MOVE TO NEXT ROW
	SUB	DI,2000H-80
	DEC	AH	; NUMBER OF ROWS To MOVE
	JNZ	R8	; CONTINUE TILL ALL MOVED

;-----	FILL IN	THE VACATED LINE(S)
R9:			; CLEAR ENTRY
	MOV	AL,BH	; ATTRIBUTE TO FILL WITH
R10:
	CALL	R18	; CLEAR THAT ROW
	SUB	DI,2000H-80	; POINT TO NEXT LINE
	DEC	BL	; NUMBER OF LINES TO FILL
	JNZ	R10	; CLEAR LOOP
	JMP	VIDEO_RETURN	; EVERYYHING DONE
RII:			; BLANK_FIELD
	MOV	BL,DH	; SET BLANK COUNT TO EVERYTHING IN FIELD
	JMP	R9	; CLEAR THE FIELD
GRAPHICS_UP	ENDP
;------------------------------------------------------
; SCROLL_DOWN
;  THIS ROUTINE SCROLLS DOWN THE INFORMATION ON THE CRT
; ENTRY --
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;------------------------------------------------------
GRAPHICS_DOWN	PROC	NEAR
	STD		; SET DIRECTION
	MOV	BL,AL	; SAVE LINE COUNT IN BL
	MOV	AX,DX	; GET LOWER RIGHT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL 	GRAPH_POSN
	MOV	DI,AX	; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW
	SUB	DX,CX
	ADD	DX,101H	; ADJUST VALUES
	SAL	DH,2	; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
			;  AND EVEN/ODD ROWS
;-----	DETERMINE CRT MODE
	CMP	@CRT_MODE,6	; TEST FOR MEDIUM RES
	JNC	R12	; FIND_SOURCE_DOWN

;-----	MEDIUM RES DOWN
	SAL	DL,1	; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI,1	; OFFSET *2 SINCE 2 BYTES/CHAR
	INC	DI	; POINT TO LAST BYTE

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R12:			; FIND_SOURCE_DOWN
	PUSH	ES	; BOTH SEGMENTS TO REGEN
	POP	DS
	SUB	CH,CH	; ZERO TO HIGH OF COUNT REGISTER
	ADD	DI,240	; POINT TO LAST ROW OF PIXELS
	SAL	BL,2	; MULTIPLY NUMBER OF LINES BY 4
	JZ	R16	; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,BL	; GET NUMBER OF LINES IN AL
	MOV	AH,80	; 80 BYTES/ROW
	MUL	AH	; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI	; SET UP SOURCE
	SUB	SI,AX	;  SUBTRACT THE OFFSET
	MOV	AH,DH	; NUMBER OF ROWS IN FIELD
	SUB	AH,BL	; DETERMINE NUMBER TO MOVE

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R13:			; ROW_LOOP_DOWN
	CALL	R17	; MOVE ONE ROW
	SUB	SI,2000H+80	; MOVE TO NEXT ROW
	SUB	DI,2000H+80
	DEC	AH	; NUMBER OF ROWS TO MOVE
	JNZ	R13	; CONTINUE TILL ALL MOVED

;-----	FILL IN THE VACATED LINE(S)
R14:			; CLEAR_ENTRY_DOWN
	MOV	AL,BH	; ATTRIBUTE TO FILL WITH
R15:			; CLEAR_LOOP_DOWN
	CALL	R18	; CLEAR A ROW
	SUB	DI,2000H+80	; POINT TO NEXT LINE
	DEC	BL	; NUMBER OF LINES TO FILL
	JNZ	R15	; CLEAR_LOOP_DOWN
	CLD		; RESET THE DIRECTION FLAG
	JMP	VIDEO_RETURN	; EVERYTHING DONE

R16:			; BLANK_FIELD_DOWN
	MOV	BL,DH	; SET BLANK COUNT TO EVERYTHING IN FIELD
	JMP	R14	; CLEAR THE FIELD
GRAPHICS_DOWN	ENDP

;-----	ROUTINE TO MOVE ONE ROW OF INFORMATION

R17	PROC	NEAR
	MOV	CL,DL	; NUMBER OF BYTES IN THE ROW
	PUSH	SI
	PUSH	DI	; SAVE POINTERS
	REP	MOVSB	; MOVE THE EVEN FIELD
	POP	DI
	POP	SI
	ADD	SI,2000H
	ADD	DI,2000H	; POINT TO THE ODD FIELD
	PUSH	SI
	PUSH	DI	; SAVE THE POINTERS
	MOV	CL,DL	; COUNT BACK
	REP	MOVSB	; MOVE THE ODD FIELD
	POP	DI
	POP	SI	; POINTERS BACK
	RET		; RETURN TO CALLER
R17	ENDP

;-----	CLEAR A SINGLE ROW

R18	PROC	NEAR
	MOV	CL,DL	; NUMBER OF BYTES IN FIELD
	PUSH	DI	; SAVE POINTER
	REP	STOSB	; STORE THE NEW VALUE
	POP	DI	; POINTER BACK
	ADD	DI,2000H	; POINT TO ODD FIELD
	PUSH	DI
	MOV	CL,DL
	REP	STOSB	; FILL THE ODD FIELD
	POP	DI
	RET		; RETURN TO CALLER
R18	ENDP
;----------------------------------------------------
; GRAPHICS WRITE
;  THIS ROUTINE WRITES THE ASCII CHARACTER TO THE CURRENT
;  POSITION ON THE SCREEN.
; ENTRY --
;  AL =	CHARACTER TO WRITE
;  BL =	COLOR ATTRIBUTE TO BE USED FOR FOREGROUND COLOR
;	IF BIT 7 IS SET, THE CHAR IS XOR'D INTO THE REGEN BUFFER
;	(0 IS USED FOR THE BACKGROUND COLOR)
;  CX =	NUMBER OF CHARS TO WRITE
;  DS =	DATA SEGMENT
;  ES =	REGEN SEGMENT
; EXIT --
;  NOTHING IS RETURNED
;
; GRAPHICS READ
;  THIS ROUTINE READS THE ASCII CHARACTER AT THE CURRENT CURSOR
;  POSITION ON THE SCREEN BY MATCHING THE DOTS ON THE SCREEN TO THE
;  CHARACTER GENERATOR CODE POINTS
; ENTRY --
;  NONE (0 IS ASSUMED AS THE BACKGROUND COLOR)
; EXIT --
;  AL =	CHARACTER READ AT THAT POSITION (0 RETURNED IF NONE FOUND)
;
; FOR BOTH ROUTINES, THE IMAGES USED TO FORM CHARS ARE CONTAINED IN ROM
;  FOR THE 1ST 128 CHARS.  TO ACCESS CHARS IN THE SECOND HALF, THE USER
;  MUST INITIALIZE THE VECTOR AT INTERRUPT 1FH (LOCATION 0007CH) TO
;  POINT TO THE USER SUPPLIED TABLE OF GRAPHIC IMAGES (8X8 BOXES).
;  FAILURE TO DO SO WILL CAUSE IN STRANGE RESULTS
;----------------------------------------------------
	ASSUME	DS:DATA,ES:DATA
GRAPHICS_WRITE	PROC	NEAR
	MOV	AH,0	; ZERO TO HIGH OF CODE POINT
	PUSH	AX	; SAVE CODE POINT VALUE


;-----	DETERMINE POSITION IN REGEN BUFFER TO PUT CODE POINTS

	CALL	S26	; FIND LOCATION IN REGEN BUFFER
	MOV	DI,AX	; REGEN POINTER IN DI

;-----	DETERMINE REGION TO GET CODE POINTS FROM

	POP	AX	; RECOVER CODE POINT
	CMP	AL,80H	; IS IT IN SECOND HALF
	JAE	SI	; YES

;-----	IMAGE IS IN FIRST HALF, CONTAINED IN ROM

	MOV	SI,OFFSET CRT_CHAR_GEN	; OFFSET OF IMAGES
	PUSH	CS	; SAVE SEGMENT ON STACK
	JMP	SHORT S2 	; DETERMINE_MODE

;-----	IMAGE IS IN SECOND HALF, IN USER MEMORY

S1:			; EXTEND_CHAR
	SUB	AL,80H	; ZERO ORIGIN FOR SECOND HALF
	PUSH	DS	; SAVE DATA POINTER
	SUB	SI,SI
	MOV	DS,SI	; ESTABLISH VECTOR ADDRESSING
	ASSUME	DS:ABS0
	LDS	SI,@EXT_PTR	; GET THE OFFSET OF THE TABLE
	MOV	DX,DS	; GET THE SEGMENT OF THE TABLE
	ASSUME	DS:DATA
	POP	DS	; RECOVER DATA SEGMENT
	PUSH	DX	; SAVE TABLE SEGMENT ON STACK

;-----	DETERMINE GRAPHICS MODE IN OPERATION

S2:			;	  DETERMINE_MODE
	SAL	AX,3	; MULTIPLY CODE POINT VALUE BY 8
	ADD	SI,AX	; SI HAS OFFSET OF DESIRED CODES
	CMP	@CRT_MODE,6
	POP	DS	; RECOVER TABLE POINTER SEGMENT
	JC	S7	; TEST FOR MEDIUM RESOLUTION MODE

;-----	HIGH RESOLUTION MODE
S3:			; HIGH_CHAR
	PUSH	DI	; SAVE REGEN POINTER
	PUSH	SI	; SAVE CODE POINTER
	MOV	DH,4	; NUMBER OF TIMES THROUGH LOOP
S4:
	LODSB		; GET BYTE FROM CODE POINTS
	TEST	BL,80H	; SHOULD WE USE THE FUNCTION
	JNZ	S6	;  TO PUT CHAR IN
	STOSB		; STORE IN REGEN BUFFER
	LODSB
S5:
	MOV	ES:[DI+2000H-1],AL	; STORE IN SECOND HALF
	ADD	DI,79	; MOVE TO NEXT ROW IN REGEN
	DEC	DH	; DONE WITH LOOP
	JNZ	S4
	POP	SI
	POP	DI	; RECOVER REGEN POINTER
	INC	DI	; POINT TO NEXT CHAR POSITION
	LOOP	S3	; MORE CHARS TO WRITE
	JMP	VIDEO_RETURN
S6:
	XOR	AL,ES:[DI]	; EXCLUSIVE OR WITH CURRENT
	STOSB		; STORE THE CODE POINT
	LODSB		; AGAIN FOR ODD FIELD
	XOR	AL,ES:[DI+2000H-1]
	JMP	S5	; BACK TO MAINSTREAM

;-----	MEDIUM RESOLUTION WRITE
S7:			;  MED_RES_WRITE
	MOV	DL,BL	; SAVE HIGH COLOR BIT
	SAL	DI,1	; OFFSET*2 SINCE 2 BYTES/CHAR
			; EXPAND BL TO FULL WORD OF COLOR
	AND	BL,3	; ISOLATE THE COLOR BITS ( LOW 2 BITS )
	MOV	AL,055H	; GET BIT CONVERSION MULTIPLIER
	MUL	BL	; EXPAND 2 COLOR BITS TO 4 REPLICATIONS
	MOV	BL,AL	; PLACE BACK IN WORK REGISTER
	MOV	BH,AL	; EXPAND TO 8 REPLICATIONS OF COLOR BITS
S8:			;  MED_CHAR
	PUSH	DI	; SAVE REGEN POINTER
	PUSH	SI	; SAVE THE CODE POINTER
	MOV	DH,4	; NUMBER OF LOOPS
S9:
	LODSB		; GET CODE POINT
	CALL	S21	; DOUBLE UP ALL THE BITS
	AND	AX,BX	; CONVERT TO FOREGROUND COLOR ( 0 BACK )
	XCHG	AH,AL	; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL,80H	; IS THIS XOR FUNCTION
	JZ	S10	; NO, STORE IT IN AS IS
	XOR	AX,ES:[DI]	; DO FUNCTION WITH LOW/HIGH
S10:
	MOV	ES:[DI],AX	; STORE FIRST BYTE HIGH, SECOND LOW
	LODSB		; GET CODE POINT 
	CALL	S21
	AND	AX,BX	; CONVERT TO COLOR
	XCHG	AH,AL	; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL,80H	; AGAIN, IS THIS XOR FUNCTION
	JZ	S11	; NO, JUST STORE THE VALUES
	XOR	AX,ES:[DI+2000H]	; FUNCTION WITH FIRST HALF LOW
S11:
	MOV	ES:[DI+2000H],AX	; STORE SECOND PORTION HIGH
	ADD	DI,80	; POINT TO NEXT LOCATION
	DEC	DH
	JNZ	S9	; KEEP GOING
	POP	SI	; RECOVER CODE POINTER
	POP	DI	; RECOVER REGEN POINTER
	INC	DI	; POINT TO NEXT CHAR POSITION
	INC	DI
	LOOP	S8	; MORE TO WRITE
	JMP	VIDEO_RETURN
GRAPHICS_WRITE	ENDP

;------------------------------------------
;	GRAPHICS READ
;------------------------------------------
GRAPHICS_READ	PROC	NEAR
	CALL	S26	; CONVERTED TO OFFSET IN REGEN
	MOV	SI,AX	; SAVE IN SI
	SUB	SP,8	; ALLOCATE SPACE FOR THE READ CODE POINT
	MOV	BP,SP	; POINTER TO SAVE AREA

;-----	DETERMINE GRAPHICS MODES

	CMP	@CRT_MODE,6
	PUSH	ES
	POP	DS	; POINT TO REGEN SEGMENT
	JC	S13	; MEDIUM RESOLUTION

;-----	HIGH RESOLUTION READ
;-----	GET VALUES FROM REGEN BUFFER AND CONVERT TO CODE POINT
	MOV	DH,4	; NUMBER OF PASSES
S12:
	MOV	AL,[SI]	; GET FIRST BYTE
	MOV	[BP],AL	; SAVE IN STORAGE AREA
	INC	BP	; NEXT LOCATION
	MOV	AL,[SI+2000H]	; GET LOWER REGION BYTE
	MOV	[BP],AL	; ADJUST AND STORE
	INC	BP
	ADD	SI,80	; POINTER INTO REGEN
	DEC	DH	; LOOP CONTROL
	JNZ	S12	; DO IT SOME MORE
	JMP	SHORT S15	; GO MATCH THE SAVED CODE POINTS

;-----	MEDIUM RESOLUTION READ
S13:			; MED_RES_READ
	SAL	SI,1	; OFFSET*2 SINCE 2 BYTES/CHAR
	MOV	DH,4	; NUMBER OF PASSES
S14:
	CALL	S23	; GET BYTES FROM REGEN INTO SINGLE SAVE
	ADD	SI,2000H-2	; GO TO LOWER REGION
	CALL	S23	; GET THIS PAIR INTO SAVE
	SUB	SI,200OH-80+2	; ADJUST POINTER SACK INTO UPPER
	DEC	DH
	JNZ	S14	; KEEP GOING INTIL ALL 5 DONE

;-----	SAVE AREA HAS CHARACTER IN IT, MATCH IT
S15:			; FIND_CHAR
	MOV	DI,OFFSET CRT_CHAR_GEN	; ESTABLISH ADDRESSING
	PUSH	CS
	POP	ES	; CODE POINTS IN CS
	SUB	BP,8	; ADJUST POINTER TO START OF SAVE AREA
	MOV	SI,BP
	CLD		; ENSURE DIRECTION
	MOV	AL,0	; CURRENT CODE POINT BEING MATCHED
S16:
	PUSH	SS	; ESTABLISH ADDRESSING TO STACK
	POP	AS	; FOR THE STRING COMPARE
	MOV	DX,128	; NUMBER TO TEST AGAINST
S17:
	PUSH	SI	; SAVE SAVE AREA POINTER
	PUSH	DI	; SAVE CODE POINTER
	MOV	CX,4	; NUMBER OF WORDS TO MATCH
	REPE	CMPSW	; COMPARE THE 8 BYTES AS WORDS
	POP	DI	; RECOVER THE POINTERS
	POP	SI
	JZ	S18	; IF ZERO FLAG SET, THEN MATCH OCCURRED
	INC	AL	; NO MATCH, MOVE ON TO NEXT
	ADD	DI,8	; NEXT CODE POINT
	DEC	DX	; LOOP CONTROL
	JNZ	S17	; DO ALL OF THEM

;-----	CHAR NOT MATCHED, MIGHT BE IN USER SUPPLIED SECOND HALF

	CMP	AL,0	;  AL<> 0 IF ONLY 1ST HALF SCANNED
	JE	S18	;  IF = 0, THEN ALL HAS BEEN SCANNED
	SUB	AX,AX
	MOV	DS,AX	;  ESTABLISH ADDRESSING TO VECTOR
	ASSUME 	DS:ABS0
	LES	DI,@EXT_PTR	;  GET POINTER
	MOV	AX,ES	;  SEE IF THE POINTER REALLY EXISTS
	OR	AX,DI	; IF ALL 0, THEN DOESN'T EXIST
	JZ	S18	; NO SENSE LOOKING
	MOV	AL,128	; ORIGIN FOR SECOND HALF
	JMP	S16	; GO BACK AND TRY FOR IT
	ASSUME	DS:DATA

;-----	CHARACTER IS FOUND ( AL=0 IF NOT FOUND )
S18:
	ADD	SP,8	; READJUST THE STACK, THROW AWAY SAVE
	JMP	VIDEO_RETURN	; ALL DONE
GRAPHICS_READ	ENDP

;----------------------------------------------
; EXPAND BYTE
;  THIS ROUTINE TAKES THE BYTE IN AL AND DOUBLES ALL
;  OF THE BITS, TURNING THE 8 BITS INTO 16 BITS.
;  THE RESULT IS LEFT IN AX
;----------------------------------------------
S21	PROC	NEAR
	PUSH	CX	; SAVE REGISTER
	MOV	CX,8	; SHIFT COUNT REGISTER FOR ONE BYTE
S22:
	ROR	AL,1	; SHIFT BITS, LOW BIT INTO CARRY FLAG
	RCR	BP,1	; MOVE CARRY FLAG (LOW BIT INTO RESULTS
	SAR	BP,1	; SIGN EXTEND HIGH BIT (DOUBLE IT)
	LOOP	S22	; REPEAT FOR ALL 8 BITS

	XCHG	AX,bP	; MOVE RESULTS TO PARAMETER REGISTER
	POP	CX	; RECOVER REGISTER
	RET		; ALL DONE
S21	ENDP
;----------------------------------------------------
; MED_READ_BYTE
; THIS ROUTINE WILL TAKE 2 BYTES FROM THE REGEN BUFFER,
;  COMPARE AGAINST THE CURRENT FOREGROUND COLOR, AND PLACE
;  THE CORRESPONDING ON/OFF BIT PATTERN INTO THE CURRENT
;  POSITION IN THE SAVE AREA
; ENTRY --
;  SI,DS = POINTER TO REGEN AREA OF INTEREST
;  BX = EXPANDED FOREGROUND COLOR
;  BP = POINTER TO SAVE AREA
; EXIT --
;  SI AND BP ARE INCREMENTED
;----------------------------------------------------
S23	PROC	NEAR
	LODSW		; GET FIRST BYTE AND SECOND BYTES
	XCHG	AL,AH	; SWAP FOR COMPARE
	MOV	CX,0C000H	; 2 BIT MASK TO TEST THE ENTRIES
	MOV	DL,0	; RESULT REGISTER
S24:
	TEST	AX,CX	; IS THIS SECTION BACKCROUND?
	JZ	S25	; IF ZERO, IT IS BACKGROUND (CARRY=0)
	STC		; WASN'T, SO SET CARRY
S25:
	RCL	DL,1	; MOVE THAT BIT INTO THE RESULT
	SHR	CX,2	; MOVE THE MASK TO THE RIGHT BY 2 BITS
	JNC	S24	; DO IT AGAIN IF MASK DIDN'T FALL OUT
	MOV	[BP],DL	; STORE RESULT IN SAVE AREA
	INC	BP	; ADJUST POINTER
	RET		; ALL DONE
S23	ENDP
;-------------------------------------------
; V4_POSITION
;  THIS ROUTINE TAKES THE CURSOR POSITION CONTAINED IN
;  THE MEMORY LOCATION, AND CONVERTS IT INTO AN OFFSET
;  INTO THE REGEN BUFFER, ASSUMING ONE BYTE/CHAR.
;  FOR MEDIUM RESOLUTION GRAPHICS, THE NUMBER MUST
;  BE DOUBLED.
; ENTRY -- NO REGISTERS,MEMORY LOCATION @CURSOR_POSN IS USED
; EXIT--
;  AX CONTAINS OFFSET INTO REGEN BUFFER
;-------------------------------------------
S26	PROC	NEAR
	MOV	AX,*CURSOR_POSN	; GET CURRENT CURSOR
GRAPH_POSN	LABEL	NEAR
	PUSH	BX	; SAVE REGISTER
	MOV	BX,AX	; SAVE A COPY OF CURRENT CURSOR
	MOV	AL,AH	; GET ROWS TO AL
	MUL	BYTE PTR @CRT_COLS	; MULTIPLY BY BYTES/COLUMN
	SHL	AX,2	; MULTIPLY * 4 SINCE 4 ROWS/BYTE
	SUB	BH,BH	; ISOLATE COLUMN VALUE
	ADD	AX,BX	; DETERMINE OFFSET
	POP	BX	; RECOVER POINTER
	RET		; ALL DONE
S26	ENDP
;--- WRITE_TTY ----------------------------------------------------------------
;				:
;   THIS INTERFACE PROVIDES A TELETYPE LIKE INTERFACE TO THE	:
;   VIDEO CARDS. THE INPUT CHARACTER IS WRITTEN TO THE CURRENT	:
;   CURSOR POSITION, AND THE CURSOR IS MOVED TO THE NEXT POSITION.	:
;   IF THE CURSOR LEAVES THE LAST COLUMN OF THE FIELD, THE COLUMN	:
;   IS SET TO ZERO, AND THE ROW VALUE IS INCREMENTED. IF THE ROW	:
;   ROW VALUE LEAVES THE FIELD, THE CURSOR IS PLACED ON THE LAST ROW,	:
;   FIRST COLUMN, AND THE ENTIRE SCREEN IS SCROLLED UP ONE LINE.	:
;   WHEN THE SCREEN IS SCROLLED UP, THE ATTRIBUTE FOR FILLING THE	:
;   NEWLY BLANKED LINE IS READ FROM THE CURSOR POSITION ON THE PREVIOUS	:
;   LINE BEFORE THE SCROLL, IN CHARACTER MODE. IN GRAPHICS MODE,	:
;   THE 0 COLOR IS USED.		:
;   ENTRY --			:
;     (AH) = CURRENT CRT MODE		:
;     (AL) = CHARACTER TO BE WRITTEN		:
;	    NOTE THAT BACK SPACE, CARRIAGE RETURN, BELL AND LINE FEED ARE	:
;	    HANDLED AS COMMANDS RATHER THAN AS DISPLAY GRAPHICS CHARACTERS	:
;     (BL) = FOREGROUND COLOR FOR CHAR WRITE IF CURRENTLY IN A GRAPHICS MODE	:
;   EXIT -- 			:
;     ALL REGISTERS SAVED		:
;------------------------------------------------------------------------------
	ASSUME	DS:DATA
WRITE_TTY	PROC	NEAR
	PUSH	AX	; SAVE REGISTERS
	PUSH	AX	; SAVE CHARACTER TO WRITE
	MOV	AH,03H
	MOV	BH,@ACTIVE_PAGE	; GET CURRENT PAGE SETTING
	INT	10H	; READ THE CURRENT CURSOR POSITION
	POP	AX	; RECOVER CHARACTER

;-----	DX NOW HAS THE CURRENT CURSOR POSITION

	CMP	AL,CR	; IS IT CARRIAGE RETURN OR CONTROL
	JBE	U8	; GO TO CONTROL CHECKS IF IT IS

;-----	WRITE THE CHAR TO THE SCREEN
U0:
	MOV	AH,0AH	; WRITE CHARACTER ONLY COMMAND
	MOV	CX,1	; ONLY ONE CHARACTER
	INT	10H	; WRITE THE CHARACTER

;-----	POSITION THE CURSOR FOR NEXT CHAR

	INC	DL
	CMP	DL,BYTE PTR @CRT_COLS	; TEST FOR COLUMN OVERFLOW
	JNZ	U7	;  SET_CURSOR
	MOV	DL,0	; COLUMN FOR CURSOR
	CMP	DH,25-1	; CHECK FOR LAST ROW
	JNZ	U6	;  SET_CURSOR_INC

;-----	SCROLL REQUIRED
U1:
	MOV	AH,02H
	INT	10H	; SET THE CURSOR

;-----	DETERMINE VALUE TO FILL WITH DURING SCROLL

	MOV	AL,@CRT_MODE	; GET THE CURRENT MODE
	CMP	AL,4
	JC	U2	; READ-CURSOR
	CMP	AL,7
	MOV	BH,0	; FILL WITH BACKGROUND
	JWE	U3	; SCROLL-UP
U2:			; READ-CURSOR
	MOV	AH,08H	; GET READ CURSOR COMMAND
	INT	10H	; READ CHAR/ATTR AT CURRENT CURSOR
	MOV	BH,AH	; STORE IN BH
U3:
	MOV	AX,0601H	; SCROLL ONE LINE
	SUB	CX,CX	; UPPER LEFT CORNER
	MOV	DH,25-1	; LOWER RIGHT ROW
	MOV	DL,BYTE PTR *CRT_COLS	; LOWER RIGHT COLUMN
	DEC	DL
U4:			; VIDEO-CALL-RETURN
	INT	10H	; SCROLL UP THE SCREEN
U5:			; TTY-RETURN
	POP	AX	; RESTORE THE CHARACTER
	JMP	VIDEO_RETURN	; RETURN TO CALLER

U6:			; SET-CURSOR-INC
	INC	DH	; NEXT ROW
U7:			; SET-CURSOR
	MOV	AH,02H
	JMP	U4	; ESTABLISH THE NEW CURSOR

-----	CHECK FOR CONTROL CHARACTERS
U8:
	JE	U9	; WAS IT A CARRIAGE RETURN
	CMP	AL,LF	; IS IT A LINE FEED
	JE	U10	; GO TO LINE FEED
	CMP	AL,07H	; IS IT A BELL
	JE	U11	; GO TO BELL
	CMP	AL,05H	; IS IT A BACKSPACE
	JNE	U0	; IF NOT A CONTROL, DISPLAY IT

;-----	BACK SPACE FOUND

	OR	DL,DL	; IS IT ALREADY AT START OF LINE
	JE	U7	; SET_CURSOR
	DEC	DX	; NO -- JUST MOVE IT BACK
	JMP	U7	; SET_CURSOR

;-----	CARRIAGE RETURN FOUND

U9:
	MOV	DL,0	; MOVE TO FIRST COLUMN
	JMP	U7	; SET-CURSOR

;-----	LINE FEED FOUND
U10:
	CMP	DH,25-1	; BOTTOM OF SCREEN
	JNE	U6	; YES, SCROLL THE SCREEN
	JMP	U1	; NO, JUST SET THE CURSOR

;-----	BELL FOUND

U11:
	MOV	CX,1331	; DIVISOR FOR 896 HZ TONE
	MOV	BL,31	; SET COUNT FOR 31/64 SECOND FOR BEER
	CALL	BEEP	; SOUND THE POD BELL
	JMP	U5	; TTY_RETURN
WRITE_TTY	ENDP
;------------------------------------------------------------------------------
; LIGHT PEN			:
;	THIS ROUTINE TESTS THE LIGHT PEN SWITCH AND THE LIGHT	:
;	PEN TRIGGER. IF BOTH ARE SET, THE LOCATION OF THE LIGHT	:
;	PEN IS DETERMINED. OTHERWISE, A RETURN WITH NO INFORMATION	:
;	IS MADE.		:
; ON EXIT:			:
;	(AH) = 0 IF NO LIGHT PEN INFORMATION IS AVAILABLE	:
;		BX,CX,DX ARE DESTROYED		:
;	(AH) = 1 IF LIGHT PEN IS AVAILABLE	:
;		(DH,DL) = ROW,CCLUMN OF CURRENT LIGHT PEN POSITION	:
;		(CH) = RASTER POSITION		:
;		(BX) = BEST GUESS AT PIXEL HORIZONTAL POSITION	:
;------------------------------------------------------------------------------
	ASSUME DS:DATA
V1	DB	3,3,5,5,3,3,3,4 	; SUBTRACT_TABLE

;-----	WAIT FOR LIGHT PEN TO BE DEPRESSED

READ_LPEN	PROC	NEAR
	MOV	AH,0	; SET NO LIGHT PEN RETURN CODE
	MOV	DX,@ADDR_6845	; GET BASE ADDRESS OF 6845
	ADD	DX,6	; POINT TO STATUS REGISTER
	IN	AL,DX	; GET STATUS REGISTER
	TEST	AL,004H	; TEST LIGHT PEN SWITCH
	JZ	V6_A	; GO IF YES
	JMP	V6	; NOT SET, RETURN

;-----	NOW TEST FOR LIGHT PEN TRIGGER

V6_A:	TEST	AL,2	; TEST LIGHT PEN TRIGGER
	JNZ	V7A	; RETURN WITHOUT RESETTING TRIGGER
	JMP 	V7

;-----	TRIGGER HAS BEEN SET, READ THE VALUE IN

V7A:
	MOV	AH,16	; LIGHT PEN REGISTERS ON 6845

;-----	INPUT REGISTERS POINTED TO BY AH, AND CONVERT TO ROW COLUMN IN (DX)

	MOV	DX,@ADDR_6845	; ADDRESS REGISTER FOR 6845
	MOV	AL,AH	; REGISTER TO READ
	OUT	DX,AL	; SET IT UP
	JMP	$+2	; I/O DELAY
	INC	DX	; DATA REGISTER
	IN	AL,DX	; GET THE VALUE
	MOV	CH,AL	; SAVE IN CX
	DEC	DX	; ADDRESS REGISTER
	INC	AH
	MOV	AL,AH	; SECOND DATA REGISTER
	OUT	DX,AL
	INC	DX	; POINT TO DATA REGISTER
	JMP	$+2	; I/O DELAY
	IN	AL,DX	; GET SECOND DATA VALUE
	MOV	AH,CH	; AX HAS INPUT VALUE

;-----	AX HAS THE VALUE READ IN FROM THE 6845

	MOV	BL,@CRT_MODE
	SUB	BH,BH	; MODE VALUE TO BX
	MOV	BL,CS:V1[BX]	; DETERMINE AMOUNT TO SUBTRACT
	SUB	AX,BX	; TAKE IT AWAY
	MOV	BX,@CRT_START
	SHR	BX,1
	SUB	AX,BX	; CONVERT TO CORRECT PAGE ORIGIN
	JNS	V2	; IF POSITIVE, DETERMINE MODE
	SUB	AX,AX	; <0 PLAYS AS 0

;-----	DETERMINE MODE OF OPERATION

V2:			; DETERMINE-MOOE
	MOV	CL,3	; SET *8 SHIFT COUNT
	CMP	@CRT-MODE,4	; DETERMINE IF GRAPHICS OR ALPHA
	JO	V4	; ALPHA_PEN
	CMP	@CRT_MODE,7
	JE	V4	; ALPHA_PEN

;-----	GRAPHICS MODE

	MOV	DL,40	; DIVISOR FOR GRAPHICS
	DIV	DL	; DETERMINE ROW(AL) AND COLUMN(AH)
			;   AL RANGE 0-99, AH RANGE 0-39
;-----	DETERMINE GRAPHIC ROW POSITION	

	MOV	CH,AL	; SAVE ROW VALUE IN CH
	ADD	CH,CH	; *2 FOR EVEN/ODD FIELD
	MOV	BL,AH	; COLUMN VALUE TO BX
	SUB	BH,BH	; MULTIPLY BY 8 FOR MEDIUM RES
	CMP	@CRT_MODE,6	; DETERMINE MEDIUM OR HIGH RES
	JNE	V3	; NOT_HIGH_RES
	MOV	CL,4	; SHIFT VALUE FOR HIGH RES
	SAL	AH,1	; COLUMN VALUE TIMES 2 FOR HIGH RES
V3:			; NOT_HIGH_RES
	SHL	BX,CL	; MULTIPLY *16 FOR HIGH RES

;-----	DETERMINE ALPHA CHAR POSITION

	MOV	DL,AH	; COLUMN VALUE FOR RETURN
	MOV	DH,AL	; ROW VALUE
	SHR	DH,2	; DIVIDE BY 4 FOR VALUE IN 0-24 RANGE
	JMP	SHORT V5	; LIGHT_PEN_RETURN_SET

;-----	ALPHA MODE ON LIGHT PEN

V4:			; ALPHA_PEN
	DIV	BYTE PTR @CRT_COLS	; DETERMINE ROW,CCLUMN VALUE
	MOV	DH,AL	; ROWS TO DH
	MOV	DL,AH	; COLS TO DL
	SAL	AL,CL	; MULTIPLY ROWS * 8
	MOV	CH,AL	; GET RASTER VALUE TO RETURN REGISTER
	MOV	BL,AH	; COLUMN VALUE
	XOR	BH,BH	;  TO BX
	SAL	BX,CL
V5:			; LIGHT_PEN_RETURN_SET
	MOV	AH,1	; INDICATE EVERY THING SET
V6:			; LIGHT_PEN_RETURN
	PUSH	DX	; SAVE RETURN VALUE (IN CASE)
	MOV	DX,@ADDR_6845	; GET BASE ADDRESS
	ADD	DX,7	; POINT TO RESET PARM
	OUT	DX,AL	; ADDRESS, NOT DATA, IS IMPORTANT
	POP	DX	; RECOVER VALUE
V7:			; RETURN_NO_RESET
	POP	BP	
	POP	DI
	POP	SI
	POP	DS	; DISCARD SAVED BX,CX,DX
	POP	DS
	POP	DS
	POP	DS
	POP	ES
	IRET
READ_LPEN	ENDP
CODE	ENDS
	END
