;V5.7	(Revised 10/17/80)
;  
;
;REMOTE CONSOLE PROGRAM FOR CP/M
;BASED ON AN ORIGINAL PROGRAM WRITTEN
;BY DAVE JAFFE, JANUARY 1979
;
;Rewritten for PMMI modem
;by Ward Christensen, February 1979
;
;Lobotomized for D C Hayes 80-103
;by Bruce Ratoff, May 1980
;
;
;I'd also like to give credit to Bill Precht
;	for the "label + offset" idea allowing
;	this program to relocate itself
;	without using DDT to initially set it up
;
;
;10/17/80 Add "ani 7f" to console output so lower-to-upper case
;	  conversion doesn't get fooled.  (BRR)
;
;10/09/80 Correct 2.X incompatibility in FCB setup for welcome file.
;	  Also fixed bug that caused loss of welcome file when /A used.
;	  (BRR)
;
;09/12/80 Reset user 0/drive A in move-up logic so BYE /A works
;	  from other drives/users.
;	  by Bruce Ratoff
;
;06/11/80 Ignore modem characters received with framing or overrun
;	  errors.  This should help eliminate some of the noise
;	  sensitivities.
;	  by Bruce Ratoff
;
;06/08/80 Added (optional) lower-to-upper case conversion on output
;	  by asking extra question at signon.
;	  by Bruce Ratoff
;
;06/04/80 Added keyboard input timeout to eliminate the "asleep
;	  at the switch" phenomenon and telco noises that sound
;	  like a carrier.
;	  by Bruce Ratoff
;
;05/26/80 Put in "warm boot command" logic to force user into
;	  a chosen CCP command and disable warm boot on entry.
;	  The JMP at location 0 is replaced by a CALL, which
;	  is used to tell BYE to disconnect instead of doing
;	  warm boot.  Program may re-enable warm boot by storing
;	  JMP opcode back into location 0.  Also, whenever
;	  warm boot is disabled, control-c is converted to null.
;	  by Bruce Ratoff
;
;05/20/80 Put test at CARCK to force valid user/drive, as
;	  suggested by Keith Petersen.
;	  by Bruce Ratoff
;
;05/12/80 Changed modem I/O equates, ports and flags for the
;	  D C Hayes 80-103A.
;	  By Bruce Ratoff
;
;01/24/80 Added routines to preserve registers when calling
;	  the user's CBIOS.  Added conditional assembly for
;	  callback feature.  Increased stack space to 60.
;	  By Keith Petersen.
;
;09/24/79 Added routines to allow automatic multiple baud
;	  rate selection, exit to CP/M from local console,
;	  echo nr. of nulls selected. By Keith Petersen,
;	  with thanks to Bob Mathias for suggestions.
;
;05/06/79 Added routine to allow "callback" operation so modem
;	  does not answer normal voice calls.  By Robbin Hough
;	  and Keith Petersen, W8SDZ.
;
;------------------------------------------------
;CHANGE THE FOLLOWING EQUATE TO AN AREA IN YOUR
;HI MEMORY WHERE THIS PROGRAM MAY PATCH ITSELF IN.
;APPROX MEMORY REQUIREMENTS: 900 BYTES.
;
DEST	EQU	0F800H	;RUNNING LOCATION OF CODE
;
;CHANGE THE FOLLOWING TO YOUR LOCAL CONSOLE KEYBOARD
;DATA PORT NUMBER.
;
CONDATA	EQU	05H	;LOCAL CONSOLE INPUT DATA PORT
;
;CHANGE THE FOLLOWING IF YOUR HAYES IS NOT AT 090H
;(THE OTHER PORT EQUATES ARE BASED ON THIS VALUE)
;
DPORT	EQU	090H	;UART DATA PORT
;
;YOU WILL LIKELY ALSO WANT TO CHANGE THE PASSWORD,
;LOCATED BELOW AT LABEL "PASSWD", AND THE MESSAGES
;PRINTED AT LABEL "WELCOME" AND JUST ABOVE LABEL
;"HANGUP"
;
;------------------------------------------------
;
;THIS PROGRAM RUNS UP IN HIGH RAM.  IT GETS THERE
;BY BEING MOVED THERE WHEN 'BYE' IS TYPED.
;
;THE PROGRAM IN HI RAM DOES THE FOLLOWING:
;
;	1.	HANGS UP THE PHONE
;	2.	AWAITS RING DETECT, ALLOWS EXIT
;		TO CP/M IF LOCAL KBD TYPES CTL-C
;	3.	OUTPUTS CARRIER
;	4.	AWAITS INCOMING CARRIER
;		GOING TO STEP 1 IF NONE
;		FOUND IN 15 SECONDS
;	5.	ASKS NUMBER OF NULLS (0-9)
;	6.	TYPES THE FILE "WELCOME" FROM
;		DISK, ALLOWING CTL-C TO SKIP IT
;	7.	ASKS FOR A PASSWORD, ALLOWING
;		5 TRIES TO GET IT RIGHT.
;	8.	WHEN PASSWORD ENTERED, DROPS
;		INTO CP/M.
;	9.	CALLER CAN LEAVE BY HANGING UP,
;		(ANY TIME CARRIER IS LOST, IT
;		WAITS 15 SECONDS, THEN GOES
;		BACK TO STEP 1), OR THE CALLER
;		MAY TYPE THE PROGRAM NAME (BYE)
;
;	SYSTEM EQUATES:
FALSE	EQU	0
TRUE	EQU	NOT FALSE
BDOS	EQU	5
CR	EQU	0DH
LF	EQU	0AH
;
MAX$DRIVE	EQU	2	;HIGHEST SUPPORTED DRIVE (0=A, 1=B, etc)
MAX$USER	EQU	3	;HIGHEST SUPPORTED USER
PRINTER	EQU	FALSE	;WANT TO RETAIN LIST DEVICE?
DUAL$IO	EQU	TRUE	;WANT CONSOLE & MODEM?
CALLBAK	EQU	TRUE	;WANT CALLBACK FEATURE?
PSWDREQ	EQU	FALSE	;PASSWORD REQUIRED?
FASTCLK	EQU	TRUE	;TRUE IF 4MHZ SYSTEM CLOCK
CPM2	EQU	TRUE	;TRUE FOR CP/M VERSION 2.x
WBCMND	EQU	TRUE	;TRUE TO USE WARM BOOT COMMAND
WBUSER	EQU	3	;USER # FOR WARM BOOT COMMAND (MUST BE 0 IN 1.4 CP/M)
WBDRV	EQU	0	;DRIVE # FOR WARM BOOT COMMAND (0=A, 1=B, ETC.)
;			 (COMMAND STRING GOES AT LABEL 'WBCSTR' NEAR
;				END OF PROGRAM)
TIMEOUT	EQU	TRUE	;TRUE IF TIMING OUT ON CONSOLE INPUT
TOVALUE	EQU	20*5*60	;TIMEOUT TIME IN 20THS OF A SECOND
;
;	HAYES MODEM PORT ASSIGNMENTS:
;
;HAYES MODEM PORT EQUATES (DPORT PREVIOUSLY DONE)
;
TPORT	EQU	DPORT+1	;CONTROL/STATUS PORT
RPORT	EQU	DPORT+1	;RATE GEN/MODEM STATUS
CPORT	EQU	DPORT+2	;MODEM CONTROL
;
;MODEM CONTROL COMMAND WORDS
;
P3CLEAR	EQU	00H	;IDLE MODE
;
;
;
;SWITCH HOOK AND MODEM COMMANDS,
;	OUTPUT TO TPORT (PORT 0)
;
P0BYE	EQU	0	;ON HOOK, OR DIALING BREAK
P0ORIG	EQU	84H	;OFF HOOK, ORIG.
P0ANSW	EQU	82H	;ANSWER PHONE
P08BIT	EQU	06H	;8 DATA BITS
P0NOPY	EQU	10H	;NO PARITY
P0EPS	EQU	01H	;EVEN PARITY SELECT
P0TSB	EQU	08H	;2 STOP BITS
P0EI	EQU	20H	;ENABLE INTERRUPTS
P0NORM	EQU	P08BIT+P0NOPY ;I USE 8 BITS, NO PARITY
P0110	EQU	P08BIT+P0NOPY+P0TSB ;SAME W/2 STOP BITS
;
;MODEM STATUS, INPUT ON TPORT (PORT 1)
;
P2RDET	EQU	80H	;RING DETECT
P2CTS	EQU	40H	;CTS (CARRIER DETECT)
;
;HAYES MODEM STATUS MASKS
;
P0TBMT	EQU	2	;XMIT BUFF EMPTY
P0DAV	EQU	1	;DATA AVAILABLE
P0RPE	EQU	4	;REC'D PARITY ERR
P0ORUN	EQU	10H	;OVERRUN
P0FERR	EQU	08H	;FRAMING ERROR
;
;BAUD RATE DIVISORS
;
B110	EQU	0	;110 BAUD
B300	EQU	1	;300 BAUD
;
	ORG	100H
;
;MOVE THE MODEM INTERFACE PROGRAM UP TO HI RAM
;AND JUMP TO IT.
;
MOVEUP	lxi	sp,80h	;set a temporary stack
	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
	push	h	;save jump address
	sub	a	;force user 0, disk A
	sta	4	;at next sign-in
	IF	CPM2
	mvi	e,0	;need to set user 0 in bdos
	mvi	c,32	;so that open succeeds on welcome file
	call	bdos
	ENDIF
	mvi	e,0
	mvi	c,14	;also need drive A default
	call	bdos
	mvi	a,0C3H	;reset boot trap/control-c trap
	sta	0
	ret		;JUMP TO "START" (was PUSHed above)
;
SOURCE	EQU	$	;BOUNDARY MEMORY MARKER
;
OFFSET	EQU	DEST-SOURCE ;RELOC AMOUNT
;-----------------------------------------------;
;	THE FOLLOWING CODE GETS MOVED		;
;	 TO HI RAM LOCATED AT "DEST",		;
;	    WHERE IT IS EXECUTED.		;
;-----------------------------------------------;
;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 HI RAM 	XX
;XX	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
;
;	IF CARRIER LOST, HANG UP, AWAIT RING.
;	OTHERWISE, SAY GOODBYE, AND HANG UP
;
START	EQU	$+OFFSET
	LXI	SP,STACK
	XRA	A	;GET 0
	STA	LOSTFLG	;SHOW NO CARR. LOST
;CHECK FOR /A OPTION ON COMMAND - REQUEST TO
;GO IMMEDIATELY INTO ANSWER MODE
	LXI	H,FCB+1	;TO OPTION
	MOV	A,M
	CPI	'/'	;OPTION?
	JNZ	NOSLASH
;GOT AN OPTION - VALIDATE IT
	INX	H	;TO OPTION BYTE
	MOV	A,M	;GET IT
	CPI	'A'	;ANSWER?
	JZ	ANSWER
;NO OPTION, OR INVALID ONE
NOSLASH	EQU	$+OFFSET
	CALL	CARCK	;SIGNED OFF W/THIS PROG?
	JC	HANGUP	;NOBODY THERE
	CALL	ILPRT	;PRINT THIS MSG:
	DB	CR,LF,'GOOD BYE, CALL AGAIN'
	DB	CR,LF,CR,LF,0
	CALL	UNPATCH	;UNDO BIOS PATCHES
;
;
;	NOBODY THERE, OR WE ARE DONE, SO HANG UP
;
HANGUP	EQU	$+OFFSET
	LXI	SP,STACK ;SET UP LOCAL STACK
	sub	a	;force user 0, disk A
	sta	4	;at next sign-in
	IF	CPM2
	mvi	e,0	;need to set user 0 in bdos
	mvi	c,32	;so that open succeeds on welcome file
	call	bdos
	ENDIF
	mvi	e,0
	mvi	c,14	;also need drive A default
	call	bdos
	mvi	a,0C3H	;reset boot trap/control-c trap
	sta	0
;
;CLEAR DTR CAUSING PHONE TO HANG UP
	MVI	A,P3CLEAR ;CLEAR..
	OUT	CPORT	;..DTR
;
;	AWAIT RINGING
;
RINGWT	EQU	$+OFFSET
;CHECK LOCAL KEYBOARD FOR CTL-C EXIT REQUEST.
;NOTE: MUST DO DIRECT INPUT BECAUSE CBIOS PATCHES
;ARE NOT DONE UNTIL CALL COMES IN.
	IN	CONDATA	;CHECK LOCAL KBD
	ANI	7FH	;STRIP PARITY BIT
	CPI	'C'-40H	;CONTROL C?
	JZ	0	;YES, --EXIT-- TO CP/M
;
RINGW2	EQU	$+OFFSET
	IN	RPORT	;GET THE STATUS
	ANI	P2RDET	;RINGING?
	JNZ	RINGWT	;NO, WAIT
;
;THE PHONE IS RINGING, NOW WAIT UNTIL RING IS FINISHED
ENDRING	EQU	$+OFFSET
	CALL	DELAY	;.1 SEC DELAY FOR DEBOUNCE
	IN	RPORT	;GET STATUS
	ANI	P2RDET	;STILL RINGING?
	JZ	ENDRING	;WAIT UNTIL RING FINISHED
;
	IF	CALLBAK	;NEXT ROUTINES IMPLEMENT CALLBACK
;
;      THIS ROUTINE MINIMIZES THE COMPUTER'S INTERFERENCE
;      WITH NORMAL HOUSEHOLD PHONE USE BY HAVING COMPUTER
;      FOLK DIAL, LET THE PHONE RING ONCE, HANG UP AND 
;      THEN DIAL AGAIN.  WHEN THE PHONE RINGS ONLY ONCE IT
;      ALERTS THE COMPUTER WHICH THEN WAITS FOR AND ANSWERS
;      ANY RING WHICH OCCURS WITHIN THE NEXT 40 SECONDS.
;
	MVI	L,45	;DELAY 4.5 SECONDS FOR NEXT RING
WAITNX	EQU	$+OFFSET
	CALL	DELAY	;WAIT .1 SECONDS
	DCR	L	;MORE TO GO?
	JNZ	WAITNX	;YES?...LOOP
	IN	RPORT	;GET THE STATUS
	ANI	P2RDET	;RINGING AGAIN?
	JNZ	EXPECT	;NO?...ITS FOR ME!
;CALL NOT FOR COMPUTER - WAIT UNTIL RINGING DONE, THEN RESET
WAITNR	EQU	$+OFFSET
	MVI	L,100	;WAIT FOR 10 SECS NO RINGING
WAITNRL	EQU	$+OFFSET
	CALL	DELAY 	;DELAY .1 SECONDS
	IN	RPORT	;GET THE STATUS
	ANI	P2RDET	;STILL RINGING?
	JZ	WAITNR	;YES, WAIT 10 MORE SECONDS
	DCR	L	;NO RING, MAYBE WE'RE DONE
	JNZ	WAITNRL	;NO, LOOP SOME MORE
	JMP	HANGUP
;
EXPECT	EQU	$+OFFSET
	LXI	H,400	;40 SECONDS TO REDIAL
RELOOK	EQU	$+OFFSET
	IN	RPORT
	ANI	P2RDET	;RINGING AGAIN?
	JZ	ANSWER
	CALL  	DELAY
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	RELOOK
	JMP	HANGUP
;
	ENDIF		;END OF CALLBACK ROUTINES
;
;SETUP MODEM
ANSWER	EQU	$+OFFSET
 	MVI	A,P0ANSW ;TURN ON
	OUT	CPORT	;..DTR
	MVI	E,20
ANSWR1	EQU	$+OFFSET
	CALL	DELAY	;GIVE TIME TO TURN ON
	DCR	E
	JNZ	ANSWR1
	MVI	A,P0110
	OUT	TPORT	;ANSWER PHONE
	CALL	DELAY	;GIVE TIME FOR ANSWER
	IN	CONDATA	;CLEAR LOCAL KBD PORT
	IN	DPORT	;CLEAR MODEM PORT
	IN	DPORT	;MAKE SURE ITS CLEAR
;OUTPUT VALUE ALLOWING MODEM TO HANG UP ON
;LOSS OF CARRIER
	CALL	CARCK	;LOOK FOR CARRIER
	JC	HANGUP	;AWAIT ANOTHER CALLER
;NOW TEST INPUT FOR BAUD RATE
	CALL	PATCH	 ;PATCH JMP TABLE
	CALL	TSTBAUD  ;SEE IF BAUD = 110
	JZ	WELCOME	 ;YES, EXIT
	MVI	A,P0NORM ;SET FOR 1 STOP BIT, ETC.
	OUT	TPORT
	MVI	A,B300+P0ANSW ;SET DIVISOR
	OUT	CPORT	 ;.. TO 300 RATE
	CALL 	TSTBAUD  ;SEE IF BAUD = 300
	JZ	WELCOME  ;YES, EXIT
	CALL	UNPATCH	 ;RESTORE ORIG BIOS JMP TBL
	JMP	ANSWER	 ;TEST MORE - INVALID BAUD RATE
;
;WELCOME TO THE SYSTEM
;
WELCOME	EQU	$+OFFSET
;
GETNULL	EQU	$+OFFSET
	CALL	ILPRT	;PRINT THIS MSG:
	DB	CR,LF
	DB	'HOW MANY NULLS DO YOU NEED? ',0
	CALL	MINPUT	;GET VALUE
	MOV	C,A
	CALL	MOUTPUT	;ECHO CHAR
	MOV	A,C
	CPI	'0'
	JC	GETNULL	;BAD, RETRY
	CPI	'9'+1
	JNC	GETNULL	;BAD
	SUI	'0'	;MAKE BINARY
	STA	NULLS	;SAVE COUNT
GETULC	EQU	$+OFFSET
	CALL	ILPRT
	DB	CR,LF,'CAN YOUR TERMINAL DISPLAY LOWER CASE? ',0
	MVI	A,20H
	STA	ULCSW	;FORCE CASE CONVERSION FOR NOW
	CALL	MINPUT	;GET Y OR N
	MOV	C,A
	CALL	MOUTPUT	;ECHO
	CPI	'N'
	JZ	DONEOPT	;WE'RE ALREADY SET UP FOR NO LOWER CASE
	CPI	'Y'
	JNZ	GETULC	;WASN'T Y OR N...RE-ASK
	SUB	A
	STA	ULCSW	;SET FLAG FOR NO CONVERSION
DONEOPT	EQU	$+OFFSET
	CALL	ILPRT
	DB	CR,LF,0
;PRINT THE WELCOME FILE
	LXI	H,WELFILN ;SOURCE
	LXI	D,FCB	;DESTINATION
	MVI	B,16	;LENGTH
	CALL	MOVE	;MOVE THE NAME
;SET DMA ADDR TO 80H
	LXI	D,80H
	MVI	C,STDMA
	CALL	BDOS
;OPEN THE WELCOME FILE
	LXI	D,FCB
	MVI	C,OPEN
	CALL	BDOS
;DID IT EXIST?
	INR	A	;A=> 0 MEANS "NO"
	JZ	PASSINT	;NO WELCOME FILE
;GOT A FILE, TYPE IT
	XRA	A	;GET 0
	STA	FCBRNO	;ZERO RECORD #
	LXI	H,100H	;GET INITIAL BUFF POINTER
;TYPE THE WELCOME FILE
WELTYLP	EQU	$+OFFSET
	CALL	RDBYTE	;GET A BYTE
	CPI	1AH	;EOF?
	JZ	PASSINT	;YES, DONE
	MOV	C,A	;SETUP FOR TYPE
	CALL	MOUTPUT	;TYPE THE CHAR
	CALL	MSTAT	;CHECK FOR..
	ORA	A	;CHAR TYPED?
	JZ	WELTYLP	;..NO, LOOP
	CALL	MINPUT	;..YES, GET CHAR
	CPI	'C'-40H	;CTL-C?
	JNZ	WELTYLP	;..NO, LOOP UNTIL EOF
;
;GET THE PASSWORD
;
PASSINT	EQU	$+OFFSET
	IF	PSWDREQ
	MVI	D,5	;5 TRIES AT PASSWORD
PASSINP	EQU	$+OFFSET
	CALL	ILPRT
	DB	CR,LF,'ENTER PASSWORD: ',0
	LXI	H,PASSWD ;POINT TO PASSWORD
	MVI	E,0	;NO MISSED LETTERS
	IN	DPORT	;CLEAR OUT GARBAGE
PWMLP	EQU	$+OFFSET
	CALL	MINPUT	;GET A CHAR
	CPI	'U'-40H	;CTL-U?
	JZ	PASSINP	;YES, RE-GET IT
	CPI	60H	;LOWER CASE?
	JC	NOTLC	;NO,
	ANI	5FH	;MAKE UPPER CASE ALPHA
NOTLC	EQU	$+OFFSET
	CMP	M	;MATCH PASSWORD?
	JZ	PWMAT	;..YES
	MVI	E,1	;..NO, SHOW MISS
	CPI	CR	;C/R?
	JNZ	PWMLP	;..NO, WAIT FOR C/R
;PASSWORD DIDN'T MATCH
PWNMAT	EQU	$+OFFSET
	CALL	ILPRT
	DB	'++INCORRECT++',CR,LF,0
	DCR	D	;MORE TRIES?
	JNZ	PASSINP	;YES
	JMP	BADPASS	;NO, GO HANG UP
;CHARACTER MATCHED IN PASSWORD
PWMAT	EQU	$+OFFSET
	INX	H	;TO NEXT CHAR
	CPI	CR	;END?
	JNZ	PWMLP	;..NO, LOOP
;END OF PASSWORD.  ANY MISSED CHARS?
	MOV	A,E	;GET FLAG
	ORA	A
	JNZ	PWNMAT	;NOT RIGHT
	ENDIF
;PASSWORD CORRECT
	CALL	ILPRT
	DB	CR,LF,'BOOTING SYSTEM...',0
	IF	WBCMND
	MVI	A,0FFH	;SET WARM BOOT FLAG (TESTED AT MOUTPUT)
	STA	WBFLAG
	ENDIF
	JMP	VWARMBT	;GO LOAD CP/M
;
;TSTBAUD ATTEMPTS TO READ A LF OR CR, RETURNS WITH
;ZERO FLAG IF THE CHARACTER READ IS ONE OF THESE TWO.
;
TSTBAUD	EQU	$+OFFSET
	CALL	MINPUT	;GET CHARACTER FROM MODEM
	CPI	CR	;IF A CARRIAGE RETURN...
	RZ		;.. RETURN
	CPI	LF	;IF A LINEFEED...
	RET		;RET ZERO FLAG, ELSE NOT ZERO
;
;	LOSS OF CONNECTION TEST
;
;THIS ROUTINE AUTOMATICALLY HANGS UP THE
;PHONE AFTER LOSS OF CARRIER
;
;THIS ROUTINE CHECKS IF CARRIER IS STILL
;PRESENT, AND IF SO, RETURNS WITH CARRY CLEAR.
;IF NOT, IT WAITS FOR CARRIER FOR 15 SECS.
;IF THIS TIME RUNS OUT, IT RETURNS WITH CARRY SET.
;
;
CARCK	EQU	$+OFFSET
	PUSH	D
	MVI	E,150
CARCK1	EQU	$+OFFSET
	IN	RPORT	;LOOK AT STATUS
	ANI	P2CTS	;GET CARRIER DETECT BIT
	JNZ	CARCK2	;IF CARRIER ON, continue with checks
;LOOP UNTIL EITHER TIME EXHAUSTED, OR
;CARRIER RETURNS
	CALL	DELAY	;KILL .1 SEC
	DCR	E	;COUNT DOWN TIME
	JNZ	CARCK1
	STC
	pop	d
	ret
CARCK2	EQU	$+OFFSET
	lda	4		;check disc/user #
	ani	0fh		;isolate drive
	cpi	max$drive+1	;valid drive?
	jc	carck3		;yes, skip this junk
	lda	4		;restore whole login byte
	ani	0f0h		;retain user #
	sta	4		;force drive to A
	jmp	0		;force warm boot (punishment?)
carck3	equ	$+offset
	if	cpm2		;skip this junk if cp/m 1.x
	lda	4		;re-fetch login byte
	ani	0f0h		;isolate user #
	cpi	max$user*16+1	;valid user ?
	jc	carck4		;yes, don't change
	lda	4		;refresh login byte again
	ani	0fh		;keep drive, zero user
	sta	4
	jmp	0		;force warm boot
	endif			;end of cp/m 2.x dependant stuff
CARCK4	EQU	$+OFFSET
	ora	a		;clear carry
	POP	D
	RET
;
;	.1 SEC DELAY ROUTINE
;
DELAY	EQU	$+OFFSET
	PUSH	B
	IF	FASTCLK
	LXI	B,16667	;.1 SEC COUNT FOR 4MHZ SYS CLOCK
	ENDIF
	IF	NOT FASTCLK
	LXI	B,8334	;.1 SEC COUNT FOR 2MHZ SYS CLOCK
	ENDIF
DELAY1	EQU	$+OFFSET
	DCX	B	;COUNT DOWN DELAY TIME
	MOV	A,B
	ORA	C
	JNZ	DELAY1	;CONTINUE COUNTING TILL DONE
	POP	B
	RET
;
;
KDELAY	EQU	$+OFFSET
	PUSH	B
	IF	FASTCLK
	LXI	B,8334	;.05 SEC COUNT FOR 4MHZ
	ENDIF
	IF	NOT FASTCLK
	LXI	B,4167	;.05 SEC COUNT FOR 2MHZ
	ENDIF
	JMP	DELAY1
;
;
;PATCH IN THE NEW JMP TABLE (SAVING THE OLD)
;
PATCH	EQU	$+OFFSET
	CALL	TBLADDR	;CALC HL= CP/M JMP TABLE
	LXI	D,VCOLDBT ;POINT TO SAVE LOCATION
	CALL	MOVE	;MOVE IT
;NOW MOVE NEW JMP TABLE TO CP/M
	CALL	TBLADDR	;CALC HL=CP/M'S JMP TABLE
	XCHG		;MOVE TO DE
	LXI	H,NEWJTBL ;POINT TO NEW
	CALL	MOVE	;MOVE IT
	RET
;
UNPATCH	EQU	$+OFFSET
	CALL	TBLADDR	;HL=CP/M'S JMP TABLE
	XCHG		;MOVE TO DE
	LXI	H,VCOLDBT ;GET SAVED TABLE
	CALL	MOVE	;MOVE ORIG BACK
	RET			
;
;CALCULATE HL=CP/M'S JUMP TABLE, B=LENGTH
;
TBLADDR	EQU	$+OFFSET
	LHLD	1	;GET BIOS POINTER
	DCX	H	;..SKIP
	DCX	H	;..TO
	DCX	H	;..COLD BOOT
;
	IF	NOT PRINTER
	MVI	B,18	;BYTES TO MOVE
	ENDIF
;
	IF	PRINTER	;RETAIN LIST DEVICE?
	MVI	B,15	;DON'T MOVE LISTER JUMP
	ENDIF
;
	RET
;
;MOVE (HL) TO (DE), LENGTH IN (B)
;
MOVE	EQU	$+OFFSET
	MOV	A,M	;GET A BYTE
	STAX	D	;PUT AT NEW HOME
	INX	D	;BUMP POINTERS
	INX	H
	DCR	B	;DEC BYTE COUNT
	JNZ	MOVE	;IF MORE, DO IT
	RET		;IF NOT,RETURN
;
;COMMON ROUTINE TO CHECK FOR CARRIER LOST,
;CALLED FROM CONSOLE STATUS, AND CONSOLE OUT
;
CHECK	EQU	$+OFFSET
	CALL	CARCK	;SEE IF CARRIER STILL ON
	RNC		;ALL OK
;CARRIER IS LOST.  TYPE MESSAGE SO LOCAL CONSOLE
;	SHOWS THE REASON
BADPASS	EQU	$+OFFSET ;COME HERE ON BAD PASSWORD
	MVI	A,1	;SHOW CARRIER LOST SO
	STA	LOSTFLG	;..WE WON'T CK AGAIN
	LXI	SP,STACK ;ENSURE VALID STACK
	CALL	ILPRT
	DB	CR,LF
	DB	'++CARRIER LOST++'
	DB	CR,LF,'   ',0
	CALL	UNPATCH	;RESTORE ORIG BIOS JMP TBL
	XRA	A	;CLEAR OUT CARRIER..
	STA	LOSTFLG	;..LOST FLAG
	JMP	HANGUP
;
;READBYTE ROUTINE - USED TO READ THE
;	WELCOME FILE
;
RDBYTE	EQU	$+OFFSET
	MOV	A,H	;TIME TO READ?
	ORA	A	;..IF AT 100H
	JZ	NORD	;NO READ REQ'D
;HAVE TO READ A SECTOR
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	ORA	A	;OK?
	MVI	A,1AH	;FAKE UP EOF
	RNZ		;RET EOF IF BAD
	LXI	H,80H
NORD	EQU	$+OFFSET
	MOV	A,M	;GET CHAR
	INX	H	;TO NEXT
	RET
;
;KEYBOARD/MODEM STATUS TEST ROUTINE
;
MSTAT	EQU	$+OFFSET
;
	IF	DUAL$IO	;WANT LOCAL CONSOLE?
	CALL	CONSTAT	;GET LOCAL STATUS
	ORA	A
	RNZ		;RET IF LOCAL CHAR
	ENDIF
;
	IN	TPORT	;GET STATUS
	ANI	P0DAV	;DATA AVAILABLE?
	RZ		;RETURN IF NOT READY
	IN	TPORT	;GET STATUS AGAIN
	ANI	18H	;CHECK FRAMING AND OVERRUN BITS
	JZ	MSTAT1	;NO ERRORS...LEGIT CHARACTER
	IN	DPORT	;SWALLOW CHARACTER (CLEARS P0DAV)
	SUB	A	;RETURN FALSE (0)
	RET
MSTAT1	EQU	$+OFFSET
	MVI	A,0FFH	;SHOW READY
	ORA	A
	RET
;
;MODEM INPUT FUNCTION, CHECKS LOCAL CONSOLE FIRST
;
MINPUT	EQU	$+OFFSET
	IF	TIMEOUT
	PUSH	H		;INITIALIZE TIMEOUT COUNTER
	LXI	H,TOVALUE
	SHLD	TOCNT
	POP	H
	ENDIF
MINPUT1	EQU	$+OFFSET
	LDA	LOSTFLG	;KNOWN LOSS..
	ORA	A	;..OF CARRIER?
	CZ	CHECK	;CARRIER STILL ON?
;
	CALL	MSTAT	;ANYTHING?
	ORA	A
;
	IF	NOT TIMEOUT
	JZ	MINPUT	;LOOP TILL CHAR RCD
	ENDIF
;
	IF	TIMEOUT
	JNZ	MINPUT2
	CALL	KDELAY		;KILL .05 SEC
	PUSH	H
	LHLD	TOCNT		;KNOCK DOWN TIMEOUT COUNTER
	DCX	H
	SHLD	TOCNT
	MOV	A,H
	ORA	L
	POP	H
	JNZ	MINPUT1		;STILL MORE TIME...KEEP TRYING
	CALL	ILPRT
	DB	'+++INPUT TIMED OUT',7,7,0
	JMP	NOSLASH
	ENDIF
;
;GOT CHAR - SEE WHICH PORT
;
MINPUT2	EQU	$+OFFSET
	IF	DUAL$IO	;BOTH LOCAL AND REMOTE?
	CALL	CONSTAT	;CHECK LOCAL CONSOLE
	ORA	A	;CHAR?
	JNZ	CONIN	;..YES, READ IT, RET.
	ENDIF
;
;LOCAL CONSOLE WASN'T READY, SO READ MODEM
	IN	DPORT	;GET DATA BYTE
	ANI	7FH	;DELETE PARITY
	JZ	MINPUT	;IGNORE NULLS
	IF	WBCMND
	CPI	3	;CONTROL-C?
	RNZ		;NO, PASS IT THRU
	LDA	0	;SEE IF WARM BOOT DISABLED
	CPI	0C3H	;JMP MEANS WARM BOOT OK
	MVI	A,3	;SO RETURN CONTROL-C AS TYPED
	RZ
	SUB	A	;ELSE CONVERT IT TO A NULL
	ENDIF
	RET
;
;MODEM OUTPUT ROUTINE.  OUTPUTS TO MODEM,
;THEN TO LOCAL CONSOLE
;
MOUTPUT	EQU	$+OFFSET
;IF WE ALREADY KNOW CARRIER IS LOST,
;DON'T CHECK FOR IT AGAIN
	LDA	LOSTFLG	;KNOWN LOSS OF CARRIER?
	ORA	A
	CZ	CHECK	;CARRIER STILL ON?
	IF	WBCMND
	LDA	WBFLAG	;IS THIS FIRST WARM BOOT SINCE SIGNON?
	ORA	A	;IF NOT, SKIP THIS STUFF
	JZ	MOUTP1
	SUB	A	;TURN OFF FLAG FOR NEXT TIME
	STA	WBFLAG
	MVI	A,0CDH	;DISABLE WARM BOOT
	STA	0
	LDA	7	;GET BDOS ADDRESS
	SUI	8	;CCP IS 2K DOWN FROM BDOS
	MOV	H,A
	MVI	L,0	;HL NOW CONTAINS CCP ENTRY ADDRESS
	PUSH	H	;SAVE FOR LATER
	MVI	L,7	;POINT TO BYTE COUNT IN CCP COMMAND BUFFER
	XCHG		;MAKE IT DEST POINTER (DE)
	LXI	H,WBCSTR	;POINT TO COMMAND STRING TO DROP IN
	MOV	B,M	;GET BYTE COUNT
	INR	B	;UP BY 2 TO INCLUDE COUNT AND TRAILING NULL
	INR	B
	CALL	MOVE	;DROP IT IN TO CCP
	MVI	A,WBUSER*16+WBDRV	;SET LOCATION 4 (USER/DRIVE)
	STA	4
	MOV	C,A	;ALSO PASS IT TO CCP
	POP	H	;GET BACK CCP ENTRY ADDRESS
	PCHL		;GO THERE
MOUTP1	EQU	$+OFFSET
	ENDIF
	IN	TPORT	;READ MODEM STATUS
	ANI	P0TBMT	;XMIT BUFF EMPTY?
	JZ	MOUTPUT	;LOOP IF NOT READY
	MOV	A,C	;GET CHAR
	ANI	7FH	;STRIP HIGH BIT
	CPI	60H	;CHECK FOR LOWER CASE
	JC	MOUTP2	;SKIP IF NOT LC
	CPI	7FH	;CHECK FOR RUBOUT
	JZ	MOUTP2
	PUSH	H
	LXI	H,ULCSW	;SUBTRACT EITHER 20H OR 0
	SUB	M
	POP	H
	MOV	C,A	;FORCE ON LOCAL AS WELL AS REMOTE
MOUTP2	EQU	$+OFFSET
	OUT	DPORT	;OUTPUT TO MODEM
;
	IF	DUAL$IO	;TO LOCAL ALSO?
	CALL	CONOUT	;SEND TO REGULAR BIOS
	ENDIF
;
;CHECK FOR NULLS
;
	CPI	LF	;TIME FOR NULLS?
	RNZ		;NO, RETURN
;SEND NULLS IF REQUIRED
	LDA	NULLS	;GET COUNT
	ORA	A	;ANY?
	RZ		;..NO
	PUSH	B
	MOV	B,A	;SAVE COUNT
	MVI	C,0	;0 IS A NULL
NULLP	EQU	$+OFFSET
	CALL	MOUTPUT	;TYPE A NULL
	DCR	B	;MORE?
	JNZ	NULLP	;..YES, LOOP
	POP	B
	RET
;
;BOOT TRAP - BECOMES DISCONNECT IF JMP AT 0 HAS BEEN ALTERED
;
MBOOT	EQU	$+OFFSET
	LDA	0	;LOOK AT OPCODE
	CPI	0C3H	;IS IT STILL JMP?
	JZ	VWARMBT	;YES, ALLOW IT
	JMP	NOSLASH	;NO, DISCONNECT
;
;	INLINE PRINT ROUTINE
;	CALL ILPRT
;	DB	'MSG',0
;
ILPRT	EQU	$+OFFSET
	XTHL		;SAVE HL, GET MSG
	PUSH	B	;SAVE
ILPLP	EQU	$+OFFSET
	MOV	C,M	;GET CHAR
	CALL	MOUTPUT	;OUTPUT IT
	INX	H	;POINT TO NEXT
	MOV	A,M	;TEST
	ORA	A	;..FOR END
	JNZ	ILPLP
	POP	B	;RESTORE
	XTHL		;RESTORE HL, RET ADDR
	RET		;RET PAST MSG
;
;ACCESS PASSWORD (ENDS IN C/R)
;
PASSWD	EQU	$+OFFSET
	DB	'******' ;THE PASSWORD ITSELF
	DB	CR	;END OF PASSWORD
;ALLOW ROOM FOR BIGGER PASSWORD TO BE
;	PATCHED IN
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0
;
;WARM BOOT COMMAND STRING
;
	IF	WBCMND
WBCSTR	EQU	$+OFFSET
	DB	WBCEND-WBCSTR-2
	DB	'RIBBS'
	DB	0
WBCEND	EQU	$+OFFSET
	ENDIF
;
;THIS AREA IS USED FOR VECTORING CALLS TO THE
;USER'S CBIOS, BUT SAVING THE REGISTERS FIRST
;IN CASE THEY ARE DESTROYED.
;
CONSTAT	EQU	$+OFFSET
	PUSH	B
	PUSH	D
	PUSH	H
	CALL	VCONSTAT
	POP	H
	POP	D
	POP	B
	RET
;
CONIN	EQU	$+OFFSET
	PUSH	B
	PUSH	D
	PUSH	H
	CALL	VCONIN
	POP	H
	POP	D
	POP	B
	RET
;
CONOUT	EQU	$+OFFSET
	PUSH	B
	PUSH	D
	PUSH	H
	CALL	VCONOUT
	POP	H
	POP	D
	POP	B
	RET
;
;	THIS IS THE JMP TABLE WHICH IS COPIED
;	ON TOP OF THE ONE POINTED TO BY
;	LOCATION 1 IN CP/M
;
NEWJTBL	EQU	$+OFFSET
	JMP	MBOOT	;BOOT TRAP
	JMP	MBOOT	;BOOT TRAP
	JMP	MSTAT	;MODEM STATUS TEST
	JMP	MINPUT	;MODEM INPUT ROUTINE
	JMP	MOUTPUT	;MODEM OUTPUT ROUTINE
	RET		;DUMMY LIST DEVICE
	NOP
	NOP
;
WELFILN	EQU	$+OFFSET
	DB	0,'WELCOME    ',0,0,0,0
;WELCOME FILE NAME ^^^^^^^^^^^
;
NULLS	EQU	$+OFFSET
	DB	5
WBFLAG	EQU	$+OFFSET
	DB	0
TOCNT	EQU	$+OFFSET
	DW	0
ULCSW	EQU	$+OFFSET
	DB	0
;
PEND	EQU	$+OFFSET ;END OF RELOCATED CODE
;
;KEEP TRACK OF LOST CARRIER WHEN TYPING
;"++CARRIER LOST++" SO WE DON'T LOOP
;
LOSTFLG	EQU	$+OFFSET
	DS	1
;
;SAVE THE CP/M JUMP TABLE HERE
;
VCOLDBT	EQU	$+OFFSET
	DS	3
VWARMBT	EQU	$+OFFSET
	DS	3
VCONSTAT EQU	$+OFFSET
	 DS	3
VCONIN	 EQU	$+OFFSET
	 DS	3
VCONOUT	 EQU	$+OFFSET
	 DS	3
VLISTOUT EQU	$+OFFSET
	 DS	3
;
	DS	60
STACK	EQU	$+OFFSET	;LOCAL STACK
;
WRCON	EQU	2
OPEN	EQU	15
READ	EQU	20
STDMA	EQU	26
FCB	EQU	5CH 
FCBRNO	EQU	FCB+32
;
	END


