;		DVRPATCH.ASM ver 1.0
;	      by Keith Petersen, W8SDZ
;		  (revised 3/14/81)
;
;This program moves alternate console and list
;drivers into high memory and then patches CP/M
;to use them instead of the regular CBIOS routines.
;The patches will remain intact until the next
;cold boot.
;
;Why this is useful: if your console and/or list
;drivers are too long to fit into your CBIOS, this
;program can be used to patch them in after CP/M is
;booted.  Another use might be when some special
;drivers are temporarily needed, such as allowing
;a modem port to be patched in so it is the console
;or the list device.
;
;------------------------------------------------
;Change the following equate to an area in your high
;memory where the alternate console and list drivers
;may be located without interference to or from CP/M.
;
DEST	EQU	0FB80H	;RUNNING LOCATION OF CODE
;
;------------------------------------------------
;
;Define I/O ports and status bits
;
CRTS	EQU	20H	;CRT STATUS PORT
CRTD	EQU	21H	;CRT DATA PORT
CRTRDY	EQU	04H	;CRT TRANS. BUF. EMPTY
KBDS	EQU	0EH	;KBD STATUS PORT
KBDD	EQU	0CH	;KBD DATA PORT
KBDRDY	EQU	01H	;KBD RECEIVE DATA AVAIL.
LISTS	EQU	02H	;LIST STATUS PORT
LISTD	EQU	03H	;LIST DATA PORT
LISRDY	EQU	80H	;LIST TRANS. BUF. EMPTY
;
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
NULL	EQU	00H	;NULL CHARACTER
BDOS	EQU	0005H	;BDOS ENTRY ADRS
;
	ORG	100H
;
;Move the console and list drivers up to high RAM
;
MOVEUP:	LXI	B,PEND-START+1		;NUMBER OF BYTES TO MOVE
	LXI	H,DEST+PEND-START+1	;END OF MOVED CODE
	LXI	D,SOURCE+PEND-START	;END OF SOURCE CODE
;
MVLP:	LDAX	D	;GET BYTE
	DCX	H	;BUMP POINTERS
	MOV	M,A	;NEW HOME
	DCX	D
	DCX	B	;BUMP BYTE COUNT
	MOV	A,B	;CHECK IF ZERO
	ORA	C
	JNZ	MVLP	;IF NOT, DO SOME MORE
;
;Now patch CP/M to use the new drivers
	LHLD	1		 ;GET CP/M JUMP TABLE ADRS
	LXI	D,4		 ;READY TO ADD 4
	DAD	D		 ;HL = CONSTAT + 1
;
	MVI	M,CSTAT AND 0FFH ;PATCH LSB OF JMP ADRS
	INX	H
	MVI	M,CSTAT SHR 8	 ;PATCH MSB JMP ADRS
;
	INX	H
	INX	H		 ;HL = CONIN + 1
	MVI	M,CONIN AND 0FFH ;PATCH LSB OF JMP ADRS
	INX	H
	MVI	M,CONIN SHR 8	 ;PATCH MSB JMP ADRS
;
	INX	H
	INX	H		 ;HL = CONOUT + 1
	MVI	M,CONOUT AND 0FFH ;MODIFY LSB JMP ADRS
	INX	H
	MVI	M,CONOUT SHR 8	 ;MODIFY MSB JMP ADRS
;
	INX	H
	INX	H		 ;HL = LISTOUT + 1
	MVI	M,LISOUT AND 0FFH ;MODIFY LSB JMP ADRS
	INX	H
	MVI	M,LISOUT SHR 8	 ;MODIFY MSB JMP ADRS
;
;Print message saying what has been done, then exit to CP/M
	LXI	D,MSG	;POINT TO MESSAGE
	MVI	C,9	;BDOS PRINT STRING FUNCTION
	JMP	BDOS	;PRINT MSG THEN RETURN TO CCP
;
MSG:	DB	'++Alternate CONSOLE and LIST '
	DB	'drivers now patched++',CR,LF,'$'
;
SOURCE	EQU	$	;BOUNDARY MEMORY MARKER
;
OFFSET	EQU	DEST-SOURCE	;RELOC AMOUNT
;-----------------------------------------------;
;	The following code gets moved		;
;	to high RAM located at "DEST"		;
;-----------------------------------------------;
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;XX   C A U T I O N :  If modifying anything 	XX
;XX 	in this program from here on: 		XX
;XX  	A-L-L  labels MUST be of the form:	XX
;XX	LABEL	EQU	$+OFFSET		XX
;XX	in order that the relocation to high   	XX
;XX	RAM work successfully.  Forgetting to	XX
;XX	specify '$+OFFSET' will cause the pro-	XX
;XX	gram to JMP into whatever is currently	XX
;XX	in low memory, with unpredictable	XX
;XX	results.  Be careful....		XX
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
;
START	EQU	$+OFFSET
;
;This is the new console status routine
CSTAT	EQU	$+OFFSET
	IN	KBDS	;GET KEYBOARD STATUS
	ANI	KBDRDY	;CHARACTER WAITING?
	MVI	A,0
	RZ		;NO, RETURN WITH 0
	CMA		;ELSE MAKE IT 0FFH
	RET
;
;This is the new console input routine
CONIN	EQU	$+OFFSET
	IN	KBDS	;GET KEYBOARD STATUS
	ANI	KBDRDY	;CHARACTER WAITING?
	JZ	CONIN	;NO, LOOP AND WAIT
	IN	KBDD	;GET CHARACTER
	ANI	7FH	;STRIP PARITY
	JZ	CONIN	;IGNORE NULLS
	RET
;
;This is the new console output routine
CONOUT	EQU	$+OFFSET
	IN	CRTS	;GET CRT STATUS
	ANI	CRTRDY	;READY FOR CHARACTER?
	JZ	CONOUT	;NO, LOOP AND WAIT
	MOV	A,C	;GET CHARACTER
	ANI	7FH	;STRIP PARITY
	RZ		;IGNORE NULLS
	CPI	7FH	;RUBOUT?
	RZ		;YES, IGNORE IT
	OUT	CRTD	;ELSE SEND TO CRT
	RET
;
;This is the new list output routine
LISOUT	EQU	$+OFFSET
	IN	LISTS	;GET LIST STATUS
	ANI	LISRDY	;READY FOR CHARACTER?
	JZ	LISOUT	;NO, LOOP AND WAIT
	MOV	A,C	;GET CHARACTER
	ANI	7FH	;STRIP PARITY
	OUT	LISTD	;SEND TO PRINTER
	CPI	LF	;WAS IT A LINE FEED?
	RNZ		;NO, RETURN
;
;Send NULL to printer to allow time for carriage to return
	MVI	C,NULL	;GET A NULL
	CALL	LISOUT	;SEND IT TO PRINTER
	MVI	C,LF	;RESTORE LINE FEED
	RET
;
PEND	EQU	$+OFFSET ;END OF RELOCATED CODE
;
	END
