;*******************************************************
;		 MEMORY MAPPED VIDEO DRIVER
;*******************************************************
;
;	    By	Bill Bolton
;		Software Tools
;		P.O. Box 80
;		Newport Beach
;		NSW, 2106
;		AUSTRALIA
;
;****** DATE OF THIS VERSION 15th February, 1980 *******
;
;	Version	1.12
;
MPAGE	EQU	0CC00H		;VDM-1 MEMORY ADDR.
SCREEN	EQU	0400H		;VDM-1 MEMORY SIZE = 1K
FINIS	EQU	004FH		;END OF CP/M SCRATCH RAM
LB	EQU	0FFH		;MASK  LOW 8 ADDR BITS
SPACE	EQU	20H		;ASCII SPACE
DELETE	EQU	7FH		;ASCII DEL
FLAG	EQU	0FFH		;FLAG SET
OFFSET	EQU	SPACE		;OFFSET TO GET ABOVE
				;CONTROL CODES
HORIZ	EQU	64		;NO. OF CHARACTERS/LINE
VERT	EQU	16		;NO. OF LINES/PAGE
;
	ORG	0F000H
;
;
;*******JUMPS MUST REMAIN HERE IN THE SAME ORDER*******
;
;
ENTRY:
	JMP	VIDEO		;PROCESS A CHARACTER
	JMP	INITIAL		;INITIALISE RAM
;
;
	DB	'VIDEODRV '
	DB	'VERSION 1.12 '
	DB	'COPYRIGHT (C) '
	DB	'1977/80 BILL BOLTON '
;
;
VIDEO:
	CALL	LIFTCUR		;GET CURSOR LOCATION
	LDA	ESCFLAG		;ESC SEQUENCE IN,
	CPI	0		;PROGRESS ?
	JNZ	ESCAPE		;YES, GO TO IT
	MOV	A,C		;GET CHAR INTO A
	ANI	07FH		;MASK OUT BIT 7 TO MAKE ASCII
	CPI	DELETE		;IS IT A DELETE ?
	JZ	EXIT		;YES, IGNORE IT
	CPI	OFFSET		;ASCII PRINTING CHAR ?
	JP	PRINT		;YES, PUT ONTO SCREEN
				;NO, MUST BE CONTROL CHARACTER
	;
	;CONTROL CHARACTER TABLE PROCESSING ROUTINE
	;
	PUSH	H		;SAVE CURRENT SCREEN LOC.
	LXI	H,VDUTBLE	;GET START OF TABLE
	ADD	L		;ADD LOW ADDR BYTE TO A
	MOV	L,A		;PUT RESULT BACK INTO L
	MOV	L,M		;GET VALUE IN MEMORY,
	XTHL			;POINTED TO BY HL INTO L,
	RET			;PUT ONTO STACK AND JUMP,
				;TO IT, ALSO RESTORE
				;CURSOR ADDR TO HL
	;
VDUTBLE:
	DB	EXIT AND LB	;^@
	DB	EXIT AND LB	;^A
	DB	EXIT AND LB	;^B
	DB	EXIT AND LB	;^C
	DB	EXIT AND LB	;^D 
	DB	EXIT AND LB	;^E
	DB	EXIT AND LB	;^F
	DB	PRINT AND LB	;^G BELL
	DB	BACKSP AND LB	;^H CURSOR LEFT
	DB	EXIT AND LB	;^I 
	DB	LINEF AND LB	;^J LINE FEED
	DB	CURSUP AND LB	;^K CURSOR UP
	DB	EOL AND LB	;^L CURSOR RIGHT
	DB	CRET AND LB	;^M CARRIAGE RETURN
	DB	EXIT AND LB	;^N
	DB	EXIT AND LB	;^O
	DB	EXIT AND LB	;^P
	DB	EXIT AND LB	;^Q
	DB	EXIT AND LB	;^R
	DB	EXIT AND LB	;^S
	DB	EXIT AND LB	;^T
	DB	EXIT AND LB	;^U
	DB	EXIT AND LB	;^V
	DB	EXIT AND LB	;^W
	DB	EXIT AND LB	;^X
	DB	EXIT AND LB	;^Y
	DB	FORM AND LB	;^Z CLEAR SCREEN
	DB	ESCF AND LB	;^[ ESCAPE
	DB	EXIT AND LB	;^\
	DB	EXIT AND LB	;^]
	DB	HOME AND LB	;^^ HOME CURSOR
	DB	CRLF AND LB	;^- NEW LINE
	;
	;PRINT THE CHARACTER ON THE SCREEN
	;
PRINT:
	MOV	C,A		;PUT ASCII CHAR BACK
	LDA	VFL		;GET REV VIDEO FLAG
	XRA	C		;XOR WITH CHARACTER
	MOV	M,A		;PUT RESULT ONTO SCREEN
	;
	;E0L CHECKS THE CURSOR POSITION FOR END OF LINE
	;
EOL:
	LDA	CURPOS		;GET CURSOR POSITION
	INR	A		;CURSOR=CURSOR+1
	CPI	HORIZ		;MAX LINE LENGTH ?
	JC	TABRET		;NO,STORE NEW CURPOS
CRLF:
	XRA	A		;RESET A TO LINE START
	STA	CURPOS		;SAVE CURSOR POSITION
	;
	;MOVE DOWN ONE LINE
	;
LINEF:
	LDA	LINENO		;GET CURRENT LINE NO.
	CPI	VERT-1		;AT THE BOTTOM ?
	JNZ	NOSCRL		;NO, DONT SCROLL UP
	;
	;SCROLL UP ONE LINE
	;
SCROLL:
	LXI	H,MPAGE+HORIZ	;YES, HL= LINE N+1
	LXI	D,MPAGE		;DE= LINE N
	LXI	B,(VERT*(HORIZ/2))-(HORIZ/2)	;CHAR COUNT
SCRL:
	MOV	A,M		;GET CHAR FROM LINE N+1
	STAX	D		;PUT IT INTO LINE N
	INX	D		;BUMP D TO NEXT CHAR POS
	INX	H		;BUMP H DITTO
	MOV	A,M		;GET IT
	STAX	D		;STORE A
	INX	H		;BUMP H
	INX	D		;BUMP D
	DCX	B		;DECR CHAR COUNT EVERY 2 MOVES
	MOV	A,B		;GET HI BYTE OF COUNT
	ORA	C		;IS COUNT = 0 ?
	JNZ	SCRL		;NOT FINISHED, DO IT AGAIN
	LDA	LINENO		;YES, GET CURRENT LINE NO.
	;
	;ERASE BOTTOM LINE (DE POINTS TO START OF LINE)
	;
EBOTL:
	XCHG			;START OF LINE INTO HL
	MVI	B,HORIZ		;SET UP CHAR COUNT
	CALL	ELOP		;DO THE ERASE
	DCR	A		;SET UP TO ENTER NOSCRL
NOSCRL:
	INR	A		;LINE = LINE + 1
	STA	LINENO		;SAVE LINE NO.
	JMP	EXIT
	;
ELOP:
	MVI	M,SPACE		;SPACE TO OVERWRITE
	INX	H		;BUMP H TO NEXT CHAR
	DCR	B		;B=0 IS END OF LINE
	JNZ	ELOP		;NO, DO IT AGAIN
	RET
	;
	;BACKSPACE
	;
BACKSP:
	LDA	CURPOS		;GET CURRENT CURSOR
	DCR	A		;CURSOR=CURSOR-1
	JP	TABRET		;STORE NEW CURPOS
	JMP	CRET 		;GONE BACK PAST START
				;OF LINE SO RESET
	;
	;CLEAR THE SCREEN AND HOME CURSOR
	;
FORM:
	CALL	CLEAR
HOME:
	XRA	A		;RESET A TO 0
	STA	LINENO		;LINENO.=0
	STA	VFL		;VFL=0
	;
	;CARRIAGE RETURN
	;
CRET:
	XRA	A		;RESET CURSOR TO LINE START
TABRET:
	STA	CURPOS
	;
	;RETURN TO THE CALLING ROUTINE
	;
EXIT:
	CALL	LIFTCUR		;CURSOR BACK ON SCREEN
	RET			;BACK TO CALLING ROUTINE
	;
	;MOVE CURSOR UP
	;
CURSUP:
	LDA	LINENO		;LINE NUMBER = N
	ANA	A		;AT TOP (N=0) ?
	JZ	EXIT		;YES, DO NOTHING
	DCR	A		;LINENO=LINENO-1
	STA	LINENO		;SAVE LINE NUMBER
	JMP	EXIT
	;
	;CLEAR THE SCREEN
	;
CLEAR:
	LXI	H,MPAGE		;GET SCREEN START ADDR.
WRSPC:
	MVI	M,SPACE		;PUT SPACE ONTO SCREEN 
	INX	H		;NEXT SCREEN LOCATION
	MOV	A,H		;GET SCREEN POS. IN A
	CPI	(MPAGE+SCREEN)/256 ;END OF SCREEN ?
	JNZ	WRSPC		;NO, WIPE NEXT SCREEN POS.
	RET
	;
	;SET ESCAPE FLAG
	;
ESCF:
	MVI	A,FLAG		;GET FLAG INTO A
	STA	ESCFLAG		;STORE ESC SEQUENCE FLAG
	JMP	EXIT
	;
	;CALCULATE MEMORY ADDRESS FROM CURSOR POSITION
	;
LIFTCUR:
	LXI	H,MPAGE-HORIZ	;SCREEN START - 1 LINE
	LXI	D,HORIZ		;CHARACTERS/LINE
	LDA	LINENO		;GET CURRENT LINE NUMBER
	INR	A		;SET UP TO ENTER CLOP
CLOP:
	DAD	D		;HL=CHAR PER LINE * NUMBER
				;OF LINES
	DCR	A		;LINENO.= LINENO.-1
	JNZ	CLOP		;LINENO.= 0 ?
CFIN:
	MOV	D,A		;YES, RESET D TO 0
	LDA	CURPOS		;GET CURSOR POSITION
	MOV	E,A		;PUT IT INTO E
	DAD	D		;ADD CHAR POS IN CURRENT
				;LINE TO TOTAL IN HL
	;
	;REVERSE THE VIDEO
	;
	MOV	A,M		;GET SCREEN POS CONTENTS
	XRI	80H		;COMPLEMENT BIT 8
	MOV	M,A		;PUT IT BACK ONTO SCREEN
	RET
	;
	;
	;SET UP ALL THE INITIAL ENTRY PARAMETERS ON
	;POWERING UP. INTENDED TO BE CALLED FROM THE
	;OPERATING SYSTEM
	;
	;
INITIAL:
	MVI	A,0		;INITIALISE VDM HARDWARE
	OUT	0C8H		;VDM-1 PORT
	LXI	H,SCRATCH	;SCRATCHPAD RAM AREA
ILOOP:
	MVI	M,00		;CLEAR ONE LOCATION
	INX	H		;NEXT LOCATION
	MOV	A,L		;GET LOW ADDRES BYTE
	CPI	FINIS		;AT THE END YET?
	JNZ	ILOOP		;NO, CLEAR NEXT BYTE
	JMP	FORM		;CLEAR THE SCREEN AND
				;SET UP THE CURSOR	
	;
	;TEST FOR WHICH CHARACTER IN ESC SEQUENCE
	;
ESCAPE:
	LDA	ESCFLAG		;FLAG DOUBLES AS COUNT
	CPI	0FFH		;1st CHARACTER ?
	JZ	ESCAPE1		;YES
	CPI	0FEH		;2nd CHARACTER ?
	JZ	ESCAPE2		;YES
	CPI	0FDH		;3rd CHARACTER ?
	JZ	ESCAPE3		;YES
	JMP	ESCEND		;NO MATCH SO END SEQ
	;
	;PROCESS FIRST CHARACTER OF ESCAPE SEQUENCE
	;
ESCAPE1:
	MOV	A,C		;GET CHAR INTO A
	CPI	'='		;IS IT '=' ?
	JZ	POSIT		;YES, SET POSFLAG
	CPI	'*'		;IS IT '*' ?
	JZ	FORM		;YES, CLEAR SCREEN
	CPI	'G'		;IS IT 'G' ?
	JZ	ATTRIB		;YES, VIDEO ATTRIB
	CPI	'T'		;IS IT 'T'
	JZ	ERASE		;YES, ERASE TO END OF LINE
	JMP	ESCEND		;NO MATCH SO END SEQ
	;
	;SET FLAG TO INDICATE POSITIONING SEQUENCE
	;
POSIT:
	MVI	A,FLAG		;SET UP POSITION FLAG
	STA	POSFLAG		;STORE IT
DECR:
	LDA	ESCFLAG		;GET ESC SEQ CHAR COUNT
	DCR	A		;COUNT = COUNT-1
	STA	ESCFLAG		;STORE IT
	JMP	EXIT
	;
	;SET FLAG TO INDICATE VIDEO ATTRIBUTE SEQUENCE
	;
ATTRIB:
	MVI	A,FLAG		;SET UP ATTRIBUTE FLAG
	STA	ATTFLAG		;STORE IT
	JMP	DECR		;DECREMENT ESCFLAG
	;
ERASE:
	MOV	A,L		;GET LOW BYTE OF SCREEN
	ORI	HORIZ-1		;SET A TO END OF LINE-1
	INR	A		;BUMP A TO EOL
	SUB	L		;SUBTRACT CURSOR LOCATION
				;FROM END OF LINE COUNT
				;TO GET NUMBER OF CHARS
				;TO ERASE
	MOV	B,A		;PUT INTO B FOR ELOP
	CALL	ELOP		;DO THE ERASE
	JMP	ESCEND		;EXIT
	;
	;PROCESS 2nd CHARACTER OF ESCAPE SEQUENCE
	;
ESCAPE2:
	LDA	POSFLAG		;GET POSITION FLAG
	CPI	FLAG		;IS IT SET ?
	JZ	NEWLINE		;YES, SET NEW LINE
	LDA	ATTFLAG		;GET ATTRIBUTE FLAG
	CPI	FLAG		;IS IT SET ?
	JZ	ATTRIB2		;YES, PROCESS IT
	JMP	ESCEND
	;
	;SET CURSOR TO NEW LINE POSITION
	;
NEWLINE:
	MOV	A,C		;GET CHAR INTO A
	SUI	OFFSET		;REMOVE OFFSET
	CPI	VERT		;LARGER THAN NO. LINES ?
	JM	CONT1		;NO
	MVI	A,VERT-1	;GET MAX. LINE NUMBER
CONT1:
	STA	LINENO		;STORE NEW LINE NO.
	JMP	DECR		;DECREMENT ESC COUNT
	;
	;SET VIDEO ATTRIBUTE
	;
ATTRIB2:
	MOV	A,C		;GET CHAR INTO A
	CPI	'0'		;IS IT ATTRIBUTE OFF?
	JZ	NORMAL		;YES, RESET VIDEO FLAG
	CPI	'4'		;IS IT REV VIDEO ON?
	JZ	REVERSE		;YES, REVERSE VIDEO FLAG
	CPI	'6'		;IS IT BLINK/REV VIDEO ON?
	JZ	REVERSE		;CANT BLINK BUT CAN REVERSE
	JMP	ESCEND
	;
	;VIDEO FLAG ROUTINES
	;
NORMAL:
	XRA	A		;RESET VIDEO FLAG
	JMP	VFLSTOR		;SAVE IT
	;
REVERSE:
	MVI	A,80H		;SET VIDEO FLAG
VFLSTOR:
	STA	VFL		;SAVE IT
	JMP	ESCEND
	;
	;PROCESS THIRD CHARACTER OF ESCAPE SEQUENCE
	;
ESCAPE3:
	LDA	POSFLAG		;GET POSITION FLAG
	CPI	FLAG		;IS IT SET ?
	JZ	NEWCHAR		;YES, SET NEW CHAR POS
	JMP	ESCEND
	;
	;SET NEW CURSOR CHARACTER POSITION
	;
NEWCHAR:
	MOV	A,C		;GET CHAR INTO A
	SUI	OFFSET		;REMOVE OFFSET
	CPI	HORIZ		;LARGER THAN LINE LENGTH ?
	JM	CONT2		;NO
	MVI	A,HORIZ-1	;GET MAX. CHAR COUNT
CONT2:
	STA	CURPOS		;SAVE NEW CHAR POS
	JMP	ESCEND
	;
	;EXIT FROM ESCAPE SEQUENCE
	;
ESCEND:
	XRA	A		;RESET A TO 0
	STA	ESCFLAG		;RESET FLAGS
	STA	POSFLAG
	STA	ATTFLAG
	JMP	EXIT
	;
	;CURSOR STORAGE LOCATIONS
	;
	ORG	0040H		;Or some other RAM scratch area
;
SCRATCH:DS	6		;THIS AREA MUST BE RAM
CURPOS:	EQU	SCRATCH		;POSITION ON LINE
LINENO:	EQU	SCRATCH+1	;LINE ON SCREEN
VFL:	EQU	SCRATCH+2	;REVERSE VIDEO FLAG
ESCFLAG:EQU	SCRATCH+3	;ESCAPE SEQUENCE FLAG
POSFLAG:EQU	SCRATCH+4	;CURSOR POSITIONING FLAG
ATTFLAG:EQU	SCRATCH+5	;VIDEO ATTRIBUTE FLAG
	;
	END	ENTRY
