;********************************************************
;*							*
;*	MEMORY-MAPPED CRT OUTPUT DRIVER			*
;*							*
;*	Russell Smith    18-August-1980			*
;*							*
;********************************************************
;
;
CRTBAS	EQU	CRTMEM.SHR.8	  ;STARTING PAGE# OF 3K CRT SPACE
CRTTOP	EQU	CRTMEM+3072.SHR.8 ;ENDING PAGE# OF CRT SPACE
;
;
CRTOUT:	PUSH	HL
	PUSH	DE
	PUSH	BC
	RES	7,A
	LD	C,A
	DI			;KEEP THE WOLVES AWAY FOR A WHILE
	LD	(SPSAVE),SP
	LD	SP,TMPSTK+32	;POINT SP TO TOP OF LOCAL STACK
	IN	A,(BITDAT)
	SET	7,A		;SELECT ROM/CRT MEMORY BANK
	OUT	(BITDAT),A
;
;	FIRST REMOVE THE OLD CURSOR CHARACTER FROM THE SCREEN
;
	LD	HL,CHRSAV	;GET CHARACTER NOW OVERLAYED BY CURSOR
	LD	B,(HL)
	LD	HL,(CURSOR)	;LOAD HL WITH CURSOR POINTER
	LD	A,H
	AND	00001111B	;A LITTLE INSURANCE THAT HL CAN'T
	OR	CRTBAS		; EVER POINT OUTSIDE THE CRT MEMORY
	LD	H,A
	LD	(HL),B		;REMOVE CURSOR BY RESTORING CHARACTER
;
;	PROCESS CHARACTER PASSED IN C
;
	CALL	OUTCH
;
;	NOW STORE A NEW CURSOR CHARACTER AT THE CURSOR LOCATION
;
	LD	A,(HL)		;GET CHARACTER AT NEW CURSOR LOCATION
	LD	(CHRSAV),A	;SAVE FOR NEXT TIME 'CRTOUT' IS CALLED
	CP	' '		;TEST IF CHARACTER IS A SPACE
	SET	7,A		;THEN TURN ON BIT 7 TO ENABLE BLINK
	JR	NZ,CRT2-$	;JUMP IF CHARACTER IS NON-BLANK
	LD	A,(CSRCHR)	; ELSE GET CHARACTER USED FOR CURSOR
CRT2:	LD	(HL),A		;STORE CHARACTER IN A AS CURSOR MARK
	LD	(CURSOR),HL	;SAVE HL AS CURSOR POINTER

	LD	SP,(SPSAVE)
	IN	A,(BITDAT)
	RES	7,A		;SWITCH BACK THE LOWER 16K OF RAM
	OUT	(BITDAT),A
	EI			;INTERRUPTS ARE SAFE AGAIN
	POP	BC
	POP	DE
	POP	HL
	RET
;
;
;
OUTCH:	LD	DE,LEADIN
	LD	A,(DE)		;GET LEAD-IN SEQUENCE STATE
	OR	A
	JP	NZ,MULTI	;JUMP IF IN A LEAD-IN SEQUENCE
	LD	A,C		; ELSE PROCESS CHARACTER IN C
	CP	' '
	JR	C,CONTRL-$	;JUMP IF A CONTROL CHARACTER
DISPLA:	LD	(HL),C		; ELSE STORE DISPLAYABLE CHARACTER
	INC	HL		; AND ADVANCE POINTER TO NEXT COLUMN
	LD	A,L
	AND	01111111B	;EXTRACT COLUMN# FROM HL
	CP	80
	RET	C		;EXIT IF NOT PAST COLUMN 79
	CALL	RETURN		; ELSE DO AUTOMATIC CARRIAGE RETURN
	CALL	LFEED		; AND LINEFEED
	RET
;
;
;
CONTRL:	PUSH	HL
	LD	HL,CTLTAB	;SEARCH FOR CONTROL CHARACTER
	LD	BC,CTLSIZ/3	; HANDLING SUBROUTINE IN TABLE
	CALL	SEARCH
	POP	HL
	RET	NZ		;EXIT IF NOT IMPLEMENTED
	PUSH	BC
	RET			;DO SNEAKY JUMP TO PRESERVE REGISTERS

CTLTAB:	DEFB	'_'-64
	DEFB	'^'-64
	DEFB	'['-64
	DEFB	'Z'-64
	DEFB	'X'-64
	DEFB	'Q'-64
	DEFB	'M'-64
	DEFB	'L'-64
	DEFB	'K'-64
	DEFB	'J'-64
	DEFB	'I'-64
	DEFB	'H'-64
	DEFB	'G'-64

	DEFW	BELL		;CTL-G IS THE BELL
	DEFW	BAKSPC		;CTL-H IS CURSOR LEFT
	DEFW	TAB		;CTL-I IS TAB
	DEFW	LFEED		;CTL-J IS CURSOR DOWN
	DEFW	UPCSR		;CTL-K IS CURSOR UP
	DEFW	FORSPC		;CTL-L IS CURSOR RIGHT
	DEFW	RETURN		;CTL-M IS CARRIAGE RETURN
	DEFW	CLREOS		;CTL-Q IS CLEAR TO END-OF-SCREEN
	DEFW	CLREOL		;CTL-X IS CLEAR TO END-OF-LINE
	DEFW	CLRSCN		;CTL-Z IS CLEAR SCREEN
	DEFW	ESCAPE		;CTL-[ IS ESCAPE
	DEFW	HOMEUP		;CTL-^ IS HOME UP
	DEFW	STUFF		;CTL-_ IS DISPLAY CONTROL CHARS

CTLSIZ	EQU	$-CTLTAB
;
;
ESCAPE:	LD	A,1
	LD	(DE),A		;SET LEAD-IN SEQUENCE STATE
	RET			; FOR XY CURSOR POSITIONING MODE
;
;
STUFF:	LD	A,4
	LD	(DE),A		;SET LEAD-IN SEQUENCE STATE
	RET			; FOR CONTROL CHAR OUTPUT MODE
;
;
BAKSPC	LD	A,L		;CHECK FOR LEFT MARGIN
	AND	01111111B
	RET	Z		;ABORT IF IN LEFTMOST COLUMN
	DEC	HL		;BACK UP CURSOR POINTER
	RET
;
;
FORSPC:	LD	A,L		;CHECK FOR RIGHTMOST COLUNM
	AND	01111111B
	CP	79
	RET	NC		;DO NOTHING IF ALREADY THERE
	INC	HL
	RET			;ELSE ADVANCE THE CURSOR POINTER
;
;
TAB:	LD	DE,8		;TABS ARE EVERY 8 COLUMNS
	LD	A,L		;GET COLUMN COMPONENT OF
	AND	01111000B	; PREVIOUS TAB POSITION
	ADD	A,E
	CP	80		;EXIT IF NEXT TAB COLUMN WOULD
	RET	NC		; BE PAST THE RIGHT MARGIN
	LD	A,L
	AND	11111000B	;ELSE INCREMENT THE CURSOR
	LD	L,A		; POINTER FOR REAL
	ADD	HL,DE
	RET
;
;
BELL:	IN	A,(BITDAT)
	SET	5,A		;TOGGLE BIT 5 OF SYSTEM PIO TO
	OUT	(BITDAT),A	; TRIGGER BELL HARDWARE TO SOUND
	RES	5,A
	OUT	(BITDAT),A
	RET
;
;
RETURN:	LD	A,L
	AND	10000000B
	LD	L,A		;MOVE CURSOR POINTER BACK
	RET			; TO START OF LINE
;
;
CLRSCN:	LD	HL,CRTMEM
	PUSH	HL
	LD	DE,CRTMEM+1
	LD	BC,24*128
	LD	(HL),' '
	LDIR			;FILL CRT MEMORY WITH SPACES
	POP	HL		;POINT TO HOME CURSOR POSITION
	LD	A,23
	LD	(BASE),A	;MAKE BASE LINE# BE 23 AND
	OUT	(SCROLL),A	; STORE IN SCROLL REGISTER
	RET
;
;
CLREOL:	PUSH	HL		;SAVE CURSOR POINTER
	LD	A,L
	AND	01111111B	;GET COLUMN# COMPONENT OF
	LD	C,A		; CURSOR POINTER INTO C
	LD	A,80		;CALCULATE HOW MANY CHARACTERS
	SUB	C		; REMAIN ON CURRENT LINE
	LD	B,A
	CALL	CLR		;CLEAR REST OF LINE @ HL
	POP	HL
	RET
;
;
CLREOS:	CALL	CLREOL		;CLEAR REMAINDER OF CURRENT ROW
	PUSH	HL
	LD	A,(BASE)
	LD	C,A		;COPY BASE SCREEN ROW# TO C
CLRS1:	LD	A,L
	RLA
	LD	A,H
	RLA			;GET ROW# COMPONENT OF HL INTO A
	AND	00011111B
	CP	C		;SEE IF HL IS AT BOTTOM ROW OF SCREEN
	JR	Z,CLRS2-$	; AND LEAVE CLEAR LOOP IF SO
	CALL	DNCSR		;ELSE POINT HL TO NEXT ROW DOWN
	CALL	CLRLIN		; AND FILL THAT LINE WITH SPACES
	JR	CLRS1-$

CLRS2:	POP	HL		;RESTORE ORIGINAL CURSOR POINTER
	RET
;
;
UPCSR:	LD	DE,-128		;SUBTRACT 1 FROM ROW# COMPONENT
	ADD	HL,DE		; OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTBAS		;CHECK FOR UNDERFLOW OF POINTER
	RET	NC
	LD	H,CRTTOP-1	;WRAP CURSOR AROUND MODULO 3K
	RET
;
;
DNCSR:	LD	DE,128		;ADD 1 TO ROW# COMPONENT
	ADD	HL,DE		; OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTTOP		;CHECK FOR OVERFLOW OF POINTER
	RET	C
	LD	H,CRTBAS	;RESET POINTER MODULO 128*24
	RET
;
;
;
LFEED:	LD	A,L
	RLA
	LD	A,H
	RLA			;EXTRACT ROW# COMPONENT OF HL
	AND	00011111B
	LD	C,A		;COPY ROW# INTO C FOR SCROLL TEST
	CALL	DNCSR		;MOVE CURSOR TO NEXT ROW DOWN
	LD	A,(BASE)	;TEST IF CURSOR WAS ON BOTTOM ROW
	CP	C		; OF SCREEN BEFORE MOVING DOWN
	RET	NZ		;EXIT IF NOT AT BOTTOM

	PUSH	HL		;ELSE PREP TO SCROLL SCREEN UP
	CALL	CLRLIN		;FILL NEW BOTTOM LINE WITH SPACES
	ADD	HL,HL
	LD	A,H		;GET ROW# COMPONENT OF HL INTO A
	AND	00011111B
	LD	(BASE),A	;STORE NEW BASE LINE#
	OUT	(SCROLL),A	;NOW SCROLL UP NEW BLANK BOTTOM LINE
	POP	HL
	RET
;
;
CLRLIN:	LD	A,L
	AND	10000000B	;POINT HL TO FIRST COLUMN OF ROW
	LD	L,A
	LD	B,80
CLR:	LD	(HL),' '	;STORE ASCII SPACES AT ADDRESS IN HL
	INC	HL		; AND INCREMENT HL
	DJNZ	CLR-$		;REPEAT NUMBER OF TIMES GIVEN BY B
	RET
;
;
HOMEUP:	LD	C,' '		;FAKE-OUT CURSOR ADDRESSING ROUTINE
	JR	SETROW-$	; TO DO HOMEUP ALMOST FOR FREE
;
;
MULTI:	EX	DE,HL		;UNCONDITIONALLY RESET THE LEAD-IN
	LD	(HL),0		; STATE TO ZERO BEFORE GOING ON
	EX	DE,HL
	CP	1
	JR	NZ,M2TST-$
SETXY:	LD	A,C		;GET SECOND CHAR OF SEQUENCE
	CP	'='
	RET	NZ		;ABORT SEQUENCE IF NOT '='
	LD	A,2
	LD	(DE),A		;MAKE LEADIN=2 NEXT TIME
	RET

M2TST:	CP	2
	JR	NZ,M3TST-$
	LD	A,3
	LD	(DE),A		;MAKE LEADIN=3 NEXT TIME
SETROW:	LD	A,(BASE)	;ARRIVE HERE ON THIRD CHARACTER
	ADD	A,C		; OF ESC,'=',ROW,COL SEQUENCE
	SUB	' '-1
SETR2:	SUB	24
	JR	NC,SETR2-$	;MAKE SURE ROW# IS BETWEEN 0 AND 23
	ADD	A,24
	OR	CRTMEM.SHR.7	;MERGE IN MSB'S OF CRT MEMORY
	LD	H,A
	LD	L,0
	SRL	H
	RR	L
	RET

M3TST:	CP	3
	JR	NZ,M4TST-$
SETCOL:	LD	A,C		;ARRIVE HERE ON FOURTH CHARACTER
	SUB	' '		; OF ESC,'=',ROW,COL SEQUENCE
SETC2:	SUB	80
	JR	NC,SETC2-$	;MAKE SURE COL# IS BETWEEN 0 AND 79
	ADD	A,80
	OR	L		;MERGE IN COL# WITH L
	LD	L,A
	RET

M4TST:	CALL	DISPLA		;DISPLAY THE CONTROL CHARACTER
	RET			; PASSED IN C
;
;
;
;
