;		MODEMPAT.ASM ver 1.0
;	      by Keith Petersen, W8SDZ
;		  (revised 4/25/82)
;
;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.
;These routines include poling of both the normal
;console port and an external serial port, allowing
;a modem to be connected to the system as a second
;console.  This program is not intended for automatic
;operation - just as a way to give temporary control
;of the system to the modem.  The patches will remain
;intact until the next cold boot.
;
;------------------------------------------------
;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
CRTTBE	EQU	04H	;CRT TRANS. BUF. EMPTY
;
KBDS	EQU	0EH	;KBD STATUS PORT
KBDD	EQU	0CH	;KBD DATA PORT
KBDRDA	EQU	01H	;KBD RECEIVE DATA AVAIL.
;
MODSTA	EQU	02H	;MODEM STATUS PORT
MODDAT	EQU	03H	;MODEM DATA PORT
MODTBE	EQU	80H	;MODEM TRANS. BUF. EMPTY
MODRDA	EQU	40H	;MODEM RECEIVE DATA AVAIL.
;
NRNULL	EQU	9	;NUMBER OF NULLS NEEDED FOR MODEM
;
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	'++Dual CONSOLE and MODEM '
	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 dual console status routine
CSTAT	EQU	$+OFFSET
	IN	MODSTA	;GET MODEM STATUS
	ANI	MODRDA	;CHARACTER WAITING?
	MVI	A,0FFH	;IN CASE ONE IS
	RNZ		;RETURN IF CHAR WAITING
	IN	KBDS	;GET KEYBOARD STATUS
	ANI	KBDRDA	;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	KBDRDA	;CHARACTER WAITING?
	JZ	MDMIN	;NO, GO CHECK MODEM
	IN	KBDD	;GET CHARACTER
	ANI	7FH	;STRIP PARITY
	JZ	CONIN	;IGNORE NULLS
	RET
;
;This is the new modem input routine
MDMIN	EQU	$+OFFSET
	IN	MODSTA	;GET MODEM STATUS
	ANI	MODRDA	;CHARACTER WAITING?
	JZ	CONIN	;NO, LOOP AND WAIT
	IN	MODDAT	;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	CRTTBE	;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
;
;This is the new modem output routine
MDMOUT	EQU	$+OFFSET
	IN	MODSTA	;GET MODEM STATUS
	ANI	MODTBE	;READY FOR CHARACTER?
	JZ	MDMOUT	;NO, LOOP AND WAIT
	MOV	A,C	;GET CHARACTER
	ANI	7FH	;STRIP PARITY
	OUT	MODDAT	;SEND TO MODEM
	CPI	LF	;WAS IT A LINE FEED?
	RNZ		;NO, RETURN
;
;Send NULL to modem to allow time for carriage to return
	PUSH	B	;SAVE LINE FEED
	MVI	B,NRNULL ;NUMBER OF NULLS
;
NULOOP	EQU	$+OFFSET
	MVI	C,NULL	;GET A NULL
	CALL	MDMOUT	;SEND IT TO MODEM
	DCR	B	;ANY MORE TO SEND?
	JNZ	NULOOP	;YES, GO SEND MORE
	POP	B	;RESTORE LINE FEED
	RET
;
LISOUT	EQU	$+OFFSET
	RET		;LIST OUTPUT DISABLED
;
PEND	EQU	$+OFFSET ;END OF RELOCATED CODE
;
ENDMARK	EQU	$+OFFSET ;! IGNORE ERROR MESSAGE
;
	END
