;********************************************************
;*							*
;*	CRT DRIVER FOR 6845 CRT CONTROLLER AND SMC 	*
;*	8002 CHARACTER GENERATOR/ATTRIBUTES CHIP	*
;*							*
;********************************************************
;
;
;	OUTPUT CHARACTER PASSED IN C TO VIDEO DISPLAY
;
CRTOUT:
	LD	HL,(ESCVEC)
	LD	A,H
	OR	L		;TEST IF IN AN ESCAPE SEQUENCE
	JR	NZ,CRT2		;JUMP IF 'ESCVEC' IS NON-ZERO

	RES	7,C		;CONFINE CHARACTERS TO 0..127 RANGE
	LD	A,C
	CP	' '
	JR	C,CRT3		;JUMP IF A CONTROL CHARACTER

	CALL	DISPLAY
	RET
;
;
CRT2:	LD	A,C		; GET THE CHARACTER
	OR	A		; NULL?
	RET	Z		; YES, IGNORE
	LD	DE,0
	LD	(ESCVEC),DE	;UNCONDITIONALLY CLEAR 'ESCVEC'
	JP	(HL)		;THEN GO TO ROUTINE @HL
;
;
;
CRT3:	LD	HL,CTLTAB
	LD	BC,CTLSIZ/3
	CALL	SEARCH		;SEARCH FOR CONTROL CHARACTER IN TABLE
	RET	NZ
	JP	(HL)
;
;
CTLTAB:	DEFB	'G'-64
	DEFB	'H'-64
	DEFB	'I'-64
	DEFB	'J'-64
	DEFB	'K'-64
	DEFB	'L'-64
	DEFB	'M'-64
	DEFB	'Q'-64
	DEFB	'X'-64
	DEFB	'Z'-64
	DEFB	'['-64
	DEFB	'^'-64
	DEFB	'_'-64

	DEFW	STUFF		;CTL-_ ... DISPLAY CONTROL CHARACTERS
	DEFW	HOMEUP		;CTL-^ ... CURSOR HOME
	DEFW	ESCAPE		;CTL-[ ... ESCAPE
	DEFW	CLRALL		;CTL-Z ... CLEAR SCREEN
	DEFW	CLREOL		;CTL-X ... CLEAR TO END OF LINE
	DEFW	CLREOS		;CTL-Q ... CLEAR TO END OF SCREEN
	DEFW	RETURN		;CTL-M ... CARRIAGE RETURN
	DEFW	RIGHT		;CTL-L ... CURSOR RIGHT
	DEFW	UPLINE		;CTL-K ... INVERSE LINEFEED/CURSOR UP
	DEFW	DNLINE		;CTL-J ... LINEFEED/CURSOR DOWN
	DEFW	HTAB		;CTL-I ... HORIZONTAL TAB
	DEFW	LEFT		;CTL-H ... BACKSPACE/CURSOR LEFT
	DEFW	BELL		;CTL-G ... AUDIBLE BELL
CTLSIZ	EQU	$-CTLTAB
;
;
;
;
;
DISPLAY:
	LD	HL,(CURSOR)
	LD	DE,CHRMEM
	ADD	HL,DE		;GET POINTER TO CHARACTER IN DISPLAY
	EX	DE,HL
	LD	HL,4096
	ADD	HL,DE		;GET POINTER TO MATCHING ATTRIBUTE
	EX	DE,HL
	LD	A,(ATTRIB)
	LD	(HL),C		;OUTPUT CHARACTER AND ATTRIBUTE
	LD	(DE),A
	LD	A,(COL)
	INC	A		;INCREMENT COLUMN NUMBER
	CP	80
	JR	NC,DISP2	;CHECK FOR AUTO NEWLINE IF AT END

	LD	(COL),A		;ELSE STORE UPDATED COLUMN#
	LD	HL,(CURSOR)
	INC	HL
	RES	3,H		;BUMP CURSOR POINTER MODULO 2048
	LD	(CURSOR),HL
	CALL	SETCSR		;MARK NEXT CURSOR POSITION
	RET
;
DISP2:	LD	HL,CRTFLG
	BIT	AUTONL,(HL)
	RET	Z		;EXIT IF AUTO NEWLINE DISABLED

	XOR	A
	LD	(COL),A		;RESET COLUMN NUMBER AND DO LINEFEED
	CALL	DNLINE
	RET
;
;
;
;
;
BELL:	LD	A,2		;TURN OFF DELAY IS 2/250 SECOND
	LD	(BELTIM),A
	LD	A,BUZZER+ON	;NOW START UP THE BELL
	OUT	(PORT0),A
	RET
;
;
;
;
;
;
ESCAPE:	LD	HL,ESCSEQ	;PROCESS SECOND CHARACTER OF ESCAPE
	LD	(ESCVEC),HL	; SEQUENCE NEXT TIME
	RET
;
;
STUFF:	LD	HL,DISPLAY
	LD	(ESCVEC),HL	;SET LEAD-IN SEQUENCE STATE
	RET			; FOR CONTROL CHAR OUTPUT MODE
;
;
;
HTAB:	LD	A,(COL)
	AND	00000111B
	LD	C,A
	LD	A,8
	SUB	C		;COMPUTE DISTANCE TO NEXT TAB STOP
	LD	D,A
	LD	E,0
	JR	CSRMOV		;ADVANCE COLUMN# AND CURSOR TO NEXT TAB
;
LEFT:	LD	DE,0FF00H	;DECREMENT COLUMN#
	JR	CSRMOV
;
RIGHT:	LD	DE,0100H	;INCREMENT COLUMN#
	JR	CSRMOV
;
UP:	LD	DE,00FFH	;DECREMENT ROW#
	JR	CSRMOV
;
DOWN:	LD	DE,0001H	;INCREMENT ROW#
CSRMOV:	LD	HL,(RC)		;GET CURRENT ROW# IN L AND COL# IN H
	LD	A,L
	ADD	A,E		;ADD -1,0,OR 1 TO ROW#
	CP	24
	RET	NC		;EXIT IF ROW OUT OF RANGE
	LD	L,A
	LD	A,H
	ADD	A,D		;ADD -1,0 OR 1 TO COLUMN#
	CP	80
	RET	NC		;EXIT IF COLUMN OUT OF BOUNDS
	LD	H,A

GOTOXY:	LD	(RC),HL		;STORE NEW ROW/COLUMN VALUES
	LD	B,0
	LD	C,H		;PUT COLUMN# INTO BC AS 16 BITS
	LD	H,0
	CALL	MULT80		;MULTILPY 16 BIT ROW# BY 80
	ADD	HL,BC		;HL=(ROW*80)+COL
	LD	DE,(START)
	ADD	HL,DE		;ADD CRTC START ADDRESS TO POINTER
	LD	A,H
	AND	00000111B	;MASK OFF FOR MODULO 2048 ROLL-AROUND
	LD	H,A
	LD	(CURSOR),HL	;STORE NEW RELATIVE CURSOR POINTER

SETCSR:	LD	A,(MOVECS)
	OR	A		;TEST IF NEW-CURSOR FLAG HAS BEEN SET
	RET	NZ		;EXIT NOW IF SO

	INC	A		;ELSE GET A ONE
	LD	(MOVECS),A	;SET FLAG FOR INTERRUPT ROUTINE
	LD	A,11010111B	;AND ENABLE VSYNC INTERRUPT FROM CTC
	OUT	(CTCA3),A
	LD	A,1
	OUT	(CTCA3),A
	RET
;
;
;
ZAPCSR:
	DI
	LD	A,00000011B
	OUT	(CTCA3),A	;DISABLE VSYNC INTERRUPT FROM CTCA3
	EI
	XOR	A
	LD	(MOVECS),A	;RESET CURSOR-MOVE FLAG
	LD	HL,(CURSOR)
	LD	DE,ATTMEM
	ADD	HL,DE		;POINT TO CURRENT CURSOR ATTRIBUTE BYTE
	LD	A,(HL)
	BIT	7,A		;TEST IF CURSOR HAS BEEN TURNED ON YET
	RET	Z		; AND EXIT IF NOT

	XOR	10010000B	;ELSE RESET BIT 7 AND INVERT BIT 4 OF
	LD	(HL),A		; CURSOR BYTE IF VSYNC HAS CHANGED THEM
	RET
;
;
;
MULT80:
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	LD	D,H
	LD	E,L		;DE=ROW*16
	ADD	HL,HL
	ADD	HL,HL		;HL=ROW*64
	ADD	HL,DE		;HL=(ROW*64)+(ROW*16)
	RET
;
;
;
UPLINE:
	LD	A,(ROW)
	OR	A
	JR	NZ,UP		;DO CURSOR-UP IF NOT ON TOP LINE
	LD	DE,-80		;ELSE SCROLL SCREEN DOWN
	LD	BC,0
	JR	LFEED
;
;
DNLINE:
	LD	A,(ROW)
	CP	23
	JR	NZ,DOWN		;DO CURSOR-DOWN IF NOT ON BOTTOM ROW
	LD	DE,80		;ELSE SCROLL SCREEN UP
	LD	BC,23*80
LFEED:	LD	HL,CRTFLG
	BIT	NOSCRL,(HL)
	RET	NZ		;EXIT IF SCROLL IS DISABLED

	LD	(NEWLIN),BC	;STORE RELATIVE ADDRESS OF NEW LINE
	LD	HL,(START)
	ADD	HL,DE		;ADD +80/-80 TO 6845 START ADDRESS REG
	LD	A,H
	AND	00000111B	;DO MODULO 2048 ROLL-AROUND
	LD	H,A
	LD	(START),HL	;STORE NEW STARTING ADDRESS

	LD	HL,(RC)
	CALL	GOTOXY		;COMPUTE NEW CURSOR AND ARM INTERRUPT

	LD	HL,(START)	;NOW DO ACTUAL LINE FEED
	LD	A,12
	OUT	(CRTADD),A	;SELECT 6845 REGSITER #12
	LD	A,H
	OUT	(CRTDAT),A	;OUTPUT MSB OF START ADDRESS
	LD	A,13
	OUT	(CRTADD),A	;SELECT 6845 REGISTER #13
	LD	A,L
	OUT	(CRTDAT),A	;OUTPUT LSB OF START ADDRESS
	LD	DE,(NEWLIN)
	ADD	HL,DE		;GET RELATIVE ADDRESS OF NEW BLANK LINE
	LD	A,H
	AND	00000111B	;MAKE SURE TO ROLL-AROUND MOD 2048
	LD	H,A
	LD	BC,80
	CALL	CLRLINE		;CLEAR NEW BOTTOM LINE ON SCREN
	RET
;
;
;
;
RETURN:	LD	HL,(RC)
	LD	H,0
	CALL	GOTOXY		;RESET COLUMN# TO ZERO
	RET
;
;
;
CLRLINE:
	LD	A,00000011B	; GET CTC RESET
	OUT	(CTCA3),A	; TURN OFF VSYNC INTERRUPT
	LD	DE,CHRMEM
	ADD	HL,DE
	EX	DE,HL
	LD	HL,4096
	ADD	HL,DE
	EX	DE,HL
	LD	A,(BLANK)
	LD	(HL),A		;STORE A BLANK AT START OF CHAR LINE
	LD	A,(ATTRIB)
	LD	(DE),A		;STORE ATTRIBUTE AT START OF ATTR LINE
	DEC	BC
	LD	A,B
	OR	C		;DECREMENT BYTECOUNT AND TEST IF=0
	JR	Z,CLRLNX	;EXIT IF DONE ALERADY

	PUSH	DE		;ELSE SAVE LINE POINTER AND BYTECOUNT
	PUSH	BC
	LD	D,H
	LD	E,L
	INC	DE
	LDIR			;FILL CHARACTER LINE WITH SPACES
	POP	BC
	POP	HL		;RESTORE POINTER AND BYTECOUNT
	LD	D,H
	LD	E,L
	INC	DE
	LDIR			;FILL ATTRIBUTE LINE WITH ATTRIB BYTE
CLRLNX:	LD	A,(MOVECS)	;GET VSYNC SETUP FLAG
	OR	A		;WANTED A VSYNC INTERRUPT?
	RET	Z		;NO
	LD	A,11010111B	;YES, GET INTERRUPT ENABLE
	OUT	(CTCA3),A	;AND SEND IT
	LD	A,1
	OUT	(CTCA3),A	;LOAD TIME CONSTANT TOO
	RET
;
;
;
CLREOS:	LD	A,23
	LD	HL,ROW
	SUB	(HL)		;COMPUTE NUMBER OF ROWS TO SCREEN-END
	LD	H,0
	LD	L,A
	CALL	MULT80		;MULTIPLY BY 80 GIVING RESULT IN HL
	EX	DE,HL
	JR	CLR2
;
CLREOL:	LD	DE,0
CLR2:	LD	A,80
	LD	HL,COL
	SUB	(HL)		;COMPUTE NUMBER OF COLUMNS TO LINE-END
	LD	H,0
	LD	L,A
	ADD	HL,DE		;SUM IS TOTAL CHARACTERS TO ERASE
	LD	B,H
	LD	C,L
	LD	HL,(CURSOR)
	CALL	CLRLINE		;GO FILL DISPLAY WITH SPACES/ATTRIBUTES
	CALL	SETCSR		;PUT CURSOR BACK IN ORIGINAL SPOT
	RET
;
;
;
CLRALL:	LD	HL,CHRMEM
	LD	DE,CHRMEM+1
	LD	BC,2047
	LD	A,(BLANK)
	LD	(HL),A
	LDIR			;FILL CHARACTER MEMORY WITH SPACES
	LD	HL,ATTMEM
	LD	DE,ATTMEM+1
	LD	BC,2047
	LD	A,(ATTRIB)
	LD	(HL),A
	LDIR			;FILL ATTRIBUTE MEMORY WITH NULL BYTES

HOMEUP:	LD	HL,0
	CALL	GOTOXY		;RESET ROW/COLUMN NUMBERS TO ZERO
	RET
;
;
;
;
;
COMPRESS:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	PUSH	BC
	LD	BC,CHRMEM
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	EX	DE,HL
	POP	BC
	LDIR			;COMPRESS CONTENTS OF CHARACTER MEMORY
	CALL	ZAPCSR
	POP	HL
	POP	DE
	LD	BC,ATTMEM
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	EX	DE,HL
	POP	BC
	LDIR			;COMPRESS CONTENTS OF ATTRIBUTE MEMORY
	RET
;
;
;
;
EXPAND:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	PUSH	BC
	LD	BC,CHRMEM
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	EX	DE,HL
	POP	BC
	LDDR			;EXPAND CONTENTS OF CHARACTER MEMORY
	CALL	ZAPCSR
	POP	HL
	POP	DE
	LD	BC,ATTMEM
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	EX	DE,HL
	POP	BC
	LDDR			;EXPAND CONTENTS OF ATTRIBUTE MEMORY
	RET
;
;
;
DELLINE:
	CALL	RETURN		;DO CARRIAGE RETURN
	LD	A,23
	LD	HL,ROW
	SUB	(HL)		;COMPUTE # OF ROWS TO END OF SCREEN
	JP	Z,CLREOL

	LD	H,0
	LD	L,A
	CALL	MULT80		;MULTIPLY BY 80
	LD	B,H
	LD	C,L		;RESULT IS BYTECOUNT FOR BLOCK MOVE
	LD	DE,(CURSOR)
	LD	HL,80
	ADD	HL,DE
	CALL	COMPRESS	;COMPRESS SCREEN TO DELETE LINE
	LD	HL,(START)
	LD	DE,23*80
	ADD	HL,DE		;GET RELATIVE START OF LAST LINE
	LD	A,H
	AND	00000111B
	LD	H,A
	LD	BC,80
	CALL	CLRLINE		;CLEAR BOTTOM LINE
	CALL	SETCSR		;RE-DISPLAY THE CURSOR
	RET
;
;
;
INSLINE:
	CALL	RETURN		;DO CARRIAGE RETURN
	LD	A,23
	LD	HL,ROW
	SUB	(HL)		;COMPUTE # OF ROWS TO END OF SCREEN
	JP	Z,CLREOL

	LD	H,0
	LD	L,A
	CALL	MULT80		;MULTIPLY BY 80
	LD	B,H
	LD	C,L		;RESULT IS BYTECOUNT FOR BLOCK MOVE
	LD	HL,(CURSOR)
	ADD	HL,BC
	DEC	HL
	EX	DE,HL
	LD	HL,80
	ADD	HL,DE
	EX	DE,HL
	CALL	EXPAND		;EXPAND SCREEN TO MAKE NEW LINE
	CALL	CLREOL		;CLEAR NEW LINE
	RET
;
;
;
DELCHAR:
	LD	HL,COL
	LD	A,79
	SUB	(HL)		;COMPUTE NUMBER OF COLUMNS LEFT IN LINE
	JP	Z,CLREOL

	LD	B,0
	LD	C,A		;USE RESULT FOR LDIR BYTECOUNT
	PUSH	BC
	LD	HL,(CURSOR)
	LD	D,H		;DE POINTS TO CHAR AT CURSOR LOCATION
	LD	E,L
	INC	HL		;HL POINTS ONE BYTE AHEAD OF THAT
	CALL	COMPRESS
	POP	BC
	LD	HL,(CURSOR)
	ADD	HL,BC
	LD	BC,1
	CALL	CLRLINE		;PUT BLANK AT END OF LINE
	CALL	SETCSR		;REPLACE DESTROYED CURSOR
	RET
;
;
;
;
INSCHAR:
	LD	HL,COL
	LD	A,79
	SUB	(HL)		;COMPUTE NUMBER OF COLUMNS LEFT IN LINE
	JP	Z,CLREOL

	LD	B,0
	LD	C,A		;USE RESULT FOR LDDR BYTECOUNT
	LD	HL,(CURSOR)
	ADD	HL,BC
	LD	D,H		;DE POINTS TO CHAR AT END OF LINE
	LD	E,L
	DEC	HL		;HL POINTS ONE BYTE BEFORE THAT
	CALL	EXPAND
	LD	HL,(CURSOR)
	LD	BC,1
	CALL	CLRLINE		;PUT A BLANK AT CURSOR LOCATION
	CALL	SETCSR		;REPLACE CURSOR
	RET
;
;
;
ESCSEQ:	LD	A,C		;PUT LEAD-IN CHARACTER IN ACC
	LD	HL,ESCTAB
	LD	BC,ESCSIZ/3
	CALL	SEARCH		;SEARCH FOR SECOND CHAR OF ESCAPE SEQ
	RET	NZ		; IN TABLE AND EXIT IF NO MATCH

	JP	(HL)		;GO TO LEAD-IN ROUTINE
;
;
ESCTAB:	DEFB	'='		;CURSOR ADDRESSING
	DEFB	'Q'		;INSERT CHARACTER
	DEFB	'W'		;DELETE CHARACTER
	DEFB	'E'		;INSERT LINE
	DEFB	'R'		;DELETE LINE
	DEFB	'*'		;CLEAR SCREEN
	DEFB	':'		;CLEAR SCREEN
	DEFB	'T'		;CLEAR TO END OF LINE
	DEFB	't'		;CLEAR TO END OF LINE
	DEFB	'Y'		;CLEAR TO END OF SCREEN
	DEFB	'y'		;CLEAR TO END OF SCREEN
	DEFB	'G'		;SET ATTRIBUTE
	DEFB	'M'		;SET GRAPHICS MODE
	DEFB	'.'		;SET PARAMETER BYTE

	DEFW	SETPARM
	DEFW	SETMODE
	DEFW	SETATTR
	DEFW	CLREOS
	DEFW	CLREOS
	DEFW	CLREOL
	DEFW	CLREOL
	DEFW	CLRALL
	DEFW	CLRALL
	DEFW	DELLINE
	DEFW	INSLINE
	DEFW	DELCHAR
	DEFW	INSCHAR
	DEFW	SETRC
ESCSIZ	EQU	$-ESCTAB
;
;
;
;
SETRC:	LD	HL,SETROW
	LD	(ESCVEC),HL
	RET
;
;
;
SETROW:	LD	A,C		;ARRIVE HERE ON THIRD CHARACTER
	SUB	' '		; OF CURSOR POSITIONING SEQUENCE
	RET	C
SETR2:	SUB	24
	JR	NC,SETR2	;MAKE SURE ROW# IS BETWEEN 0 AND 23
	ADD	A,24
	LD	(ROWTMP),A	;SAVE TEMPORARY ROW#
	LD	HL,SETCOL
	LD	(ESCVEC),HL	;GO TO 'SETCOL' ON NEXT CHARACTER
	RET
;
;
;
SETCOL:	LD	A,C		;ARRIVE HERE ON FOURTH CHARACTER
	SUB	' '		; OF ESC,'=',ROW,COL SEQUENCE
	RET	C
SETC2:	SUB	80
	JR	NC,SETC2	;MAKE SURE COL# IS BETWEEN 0 AND 79
	ADD	A,80
	LD	H,A		;PUT COLUMN# INTO H
	LD	A,(ROWTMP)
	LD	L,A		;PUT ROW# INTO L
	CALL	GOTOXY		;GO COMPUTE RELATIVE CURSOR POINTER
	RET
;
;
;
;
SETATTR:
	LD	HL,SATTR2
	LD	(ESCVEC),HL
	RET
;
;
;
SATTR2:
	LD	A,C
	CALL	ASCHEX		;CONVERT THIRD CHAR IN SEQUENCE TO HEX
	RET	C		;EXIT IF OUT OF RANGE

	LD	B,0
	LD	C,A
	LD	HL,BLANK
	LD	(HL),' '	;USE ASCII SPACE FOR BLANKS
	LD	A,00000011B	;USE ALPHANUMERIC ATTRIBUTE MODE
	BIT	3,C		;TEST IF ALPHA OR GRAPHICS MODE
	JR	Z,SATTR3
	LD	(HL),0		;SWITCH TO ZEROS FOR BLANKS AND LOAD
	LD	A,(GRMODE)	; A WITH SELECTED GRAPHICS MODE BITS
SATTR3:	LD	HL,BITTAB
	RES	3,C
	ADD	HL,BC		;INDEX INTO BIT TABLE FOR ATTRIBUTES
	OR	(HL)		;MERGE MODE AND ATTRIBUTE BITS
	LD	(ATTRIB),A	;STORE NEW ATTRIBUTE BYTE
	RET
;
;
;
BITTAB:	DEFB	0
	DEFB	UNDLINE
	DEFB	BLINK
	DEFB	BLINK+UNDLINE
	DEFB	REVERSE
	DEFB	REVERSE+UNDLINE
	DEFB	REVERSE+BLINK
	DEFB	REVERSE+BLINK+UNDLINE
;
;
;
;
SETMODE:
	LD	HL,SMODE2
	LD	(ESCVEC),HL
	RET
;
;
;
SMODE2:
	LD	A,C
	AND	00000011B
	LD	(GRMODE),A	;STORE BITS FOR GRAPHICS MODE FIELD
	RET			; OF ATTRIBUTE BYTE
;
;
;
SETPARM:
	LD	HL,SPARM2
	LD	(ESCVEC),HL
	RET
;
;
SPARM2:
	LD	A,C
	CALL	ASCHEX
	RET	C

	LD	C,A
	AND	00000111B
	INC	A		;TRANSFORM CODE TO NUMBER IN RANGE 1..8
	LD	B,A
	XOR	A
	SCF
SPARM3:	RLA
	DJNZ	SPARM3		;DERRIVE BIT POSITION FROM CODE BYTE
	LD	HL,CRTFLG
	BIT	3,C		;TEST IF PARAM BIT IS TO BE SET/RESET
	JR	Z,SPARM4

	OR	(HL)		;SET PARAMETER BIT @HL
	LD	(HL),A
	RET
;
SPARM4:	CPL
	AND	(HL)		;RESET PARAMETER BIT @HL
	LD	(HL),A
	RET
;
;
;

