
VERSION	EQU	740	;MDM740 (05/07/84)  --	CP/M MODEM PROGRAM  *
;
;
;     NOTE:  This program is written in Zilog Z80 code.
;	     When assembled, it will run on 8080, 8085
;	     or Z80 computers.
;
;		COPYRIGHTED 1984 BY IRVIN M. HOFF
;
; THIS TELEPHONE MODEM PROGRAM USES THE CHRISTENSEN PROTOCOL.  IT HAS
; BOTH 'CRC' AND CHECKSUM CAPABILITY FOR ERROR-DETECTION. IT SUPPORTS
; DIALING AND AUTO-REDIALING FOR THE ANCHOR AUTOMATION SIGNALMAN MARK
; XII, THE PROMETHEUS PROMODEM, THE HAYES 300 AND 1200, U.S. ROBOTICS
; MODEMS AND THE PMMI S-100 PLUG IN MODEM.  IT SUPPORTS UP TO TWO AL-
; TERNATE DIALING SYSTEMS SUCH AS 'MCI', 'SPRINT', ETC.
;
; OTHER EXTERNAL MODEMS MAY BE READILY USED, ALTHOUGH  MANUAL DIALING
; MAY BE NECESARY.  OVERLAYS HAVE BEEN MADE ALLOWING RAPID ADAPTATION
; TO VARIOUS COMPUTERS.  THE PROGRAM CONFORMS READILY TO NUMEROUS I/O
; DEVICES INCLUDING THE 2661, 8250, 8251, Z80-SIO, ETC.
;
; NOTE:  Current version is 73 sectors long. Use this figure when using
;	 DDT, etc. to merge the appropriate overlay, regardless of what
;	 the overlay may call for (such as 66 sectors for overlays made
;	 when the program was not as lengthy.)
;
;***********************************************************************
;
;	   THIS PROGRAM IS COPYRIGHTED.  NO CHANGES MAY BE MADE
;	   WITHOUT PERMISSION UNLESS FOR PERSONAL USE ONLY.  IT
;	   MAY BE DISTRIBUTED IN ITS ORIGINAL FORM WITH NO PRE-
;	   VIOUS WRITTEN PERMISSION WITH THE UNDERSTANDING THAT
;	   NO FINANCIAL GAIN WILL RESULT FROM SUCH TRANSACTION.
;
;					- Irvin M. Hoff
;
;***********************************************************************
;
;   GENERAL INTEREST:  When transferring files modem-to-modem, the batch
;   mode is extremely useful.  It allows automatic transmission of nu-
;   merous files while the operator at the receiving end does virtually
;   nothing.  It can be used for single files or with wildcards.  With
;   normal single program transfer, the receiving end switches from CRC
;   to checksum in one minute and times out completely in 120 seconds.
;   (In batch mode it times out in 3 minutes for receive.)  This offers
;   ample opportunity to transfer programs between individuals.
;
;	  M7NM-6.ASM can be used to change the telephone overlay numbers
;	     and/or set the alternate dialing system code (also used to
;	     change HEXSHO and SAVSIZ, mentioned below.)
;
;	  M7LIB.COM  can be used to very easily and very quickly change
;	     any of the telephone overlay numbers.
;
;	  M7FNK.COM can be used to quickly and easily change any of the
;	     10 function key assignments (or the function key intercept
;	     character itself, which is currently the '^' character.
;
;	Significant address changes now used:
;
;	   0DFEH  -  HEXSHO  00 = do not show hex record count
;			     FF = show both hex and decimal count
;	   0DFFH  -  SAVSIZ  20 = 4k file transfer buffer size
;			     40 = 8k file transfer buffer size
;			     80 = 16k file transfer buffer size
;	   0E00H  -  NUMLIB  (start of telephone number library)
;
;***********************************************************************
;
;	In past years many people have contributed ideas for modem pro-
;	grams that I have adapted for use in the MDM7 series.  The list
;	below includes as many of those as I can recall -- Frank Gaude'
;	deserving special mention:
;
;	     Ward Christensen, Jim Mills, Mark Zeigler, Keith Petersen,
;	     Paul Kelly, Bruce Ratoff, John Mahr, Rich Berg, Bob Clyne,
;	     Bill Earnest, Paul Hansknecht, Ron Fowler, Fred Viles, Bob
;	     Plouffe, Ben Bronson, Sigi Kluger, Frank Gaude' and likely
;	     others.
;					- Irv Hoff W6FFC
;
;***********************************************************************
;
; 05/07/84  Numerous small bugs fixed that have apparently been present
;  MDM740   since MODEM7 days.	Copyright notice added when the program
;	    is brought up, to be legal.  Auto-linking added which gives
;	    the ability to automatically redial a sequential list up to
;	    32 numbers.  Two options:
;
;			B>>COMMAND:  A,F,4,G,Z
;						  or
;			B>>COMMAND:  A/F/4/G/Z
;
;	    The '/' beeps just once when connected and then jumps auto-
;	    matically to the terminal mode to catch any signon messages
;	    from IBM or Unix systems, etc.  The first option beeps con-
;	    tinuously until the operator hits any key.	This places the
;	    program in the terminal mode.  These two options combine to
;	    make MDM740 one of the most useful modem programs currently
;	    available for dialing busy numbers.
;					- Irv Hoff
;
; 01/01/83  First version.  Can be assembled with ASM.COM.  (Previously
;  MDM700   it was necessary to have the MODEM7.LIB file and use MAC.COM
;	    to assemble the program.)  Selected MDM700 as a new program
;	    to allow me to make changes that I felt might be beneficial.
;	    This would not hinder others from updating existing programs
;	    to their own satisfaction.	The name was also selected so it
;	    would fit on databanks limiting file names to 6 characters.
;					- Irv Hoff
;
;***********************************************************************
;
;
PORT	EQU	0C0H		;your base port (data or status)
;
MDCTL1	EQU	PORT		;modem control port
MDDATP	EQU	PORT+1		;modem data port
MDRCVB	EQU	02H		;modem receive bit (DAV)
MDRCVR	EQU	02H		;modem receive ready
MDSNDB	EQU	01H		;modem send bit
MDSNDR	EQU	01H		;modem send ready bit
;
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;		     special equates for PMMI
;
MDCTL2	EQU	PORT+3		;modem status port
;
BAUDRP	EQU	PORT+2		;modem baud rate port
BRKMASK	EQU	0FBH		;mask to set break
EVPAMSK	EQU	20H		;mask to set even parity
NOPAMSK	EQU	10H		;mask to reset to no parity
ODPAMSK	EQU	0CFH		;mask to set odd parity
;
ANSWMOD	EQU	1EH		;answer mode
ORIGMOD	EQU	1DH		;originate mode
WAITCTS	EQU	150		;number of seconds (x5) to wait for the
				;computer to answer after PMMI auto-dial
				;100=20 sec, 150=30 sec, 255=51 seconds
				;any number 0-255 acceptable
;
;		  (end of special PMMI eauates)
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;
YES	EQU	0FFH
NO	EQU	0
;
;
BUFSIZ	EQU	16		;buffer size in Kbytes for ASCII capture to disk
				;(16k is one file extent)
XFRSIZ	EQU	4		;file transfer buffer in Kbytes.  Do not
				;make any larger than BUFSIZ.  16k works
				;fine on all but very slowest systems
BDNMCH	EQU	75H		;bad name match
ERRLIM	EQU	10		;maximum allowable consecutive errors
ERRCRC	EQU	6		;CRC tries, then switches to CHECKSUM
LIBLEN	EQU	34		;length of each phone library entry
SHOWHEX	EQU	NO		;yes, show both decimal and hex record
				;counts - no, show just standard decimal
				;record count
RUB	EQU	7FH		;rub
CRC	EQU	'C'		;requests 'CRC' instead of 'CKSUM'
ESC	EQU	'['-40H		;^[ = escape
SOH	EQU	'A'-40H		;^A = start of header
EOT	EQU	'D'-40H		;^D = end of text
EXITCHR	EQU	'E'-40H		;^E = exit character
ACK	EQU	'F'-40H		;^F = acknowledge
OKNMCH	EQU	'F'-40H		;^F = ok name match
BELL	EQU	'G'-40H		;^G = bell character
BKSP	EQU	'H'-40H		;^H = backspace
LF	EQU	'J'-40H		;^J = linefeed
CR	EQU	'M'-40H		;^M = carriage return
XON	EQU	'Q'-40H		;^Q = XON character
XOFF	EQU	'S'-40H		;^S = XOFF character
NAK	EQU	'U'-40H		;^U = not acknowledge
CANCEL	EQU	'X'-40H		;^X = cancel send or receive
EOFCHAR	EQU	'Z'-40H		;^Z = end of file
;
;
	ORG	0100H
;
;
	JP	START		;skip the data area below
;
;
; These routines and equates are at the beginning of the program so
; they can be patched by a monitor or overlay file without re-assembling
; the program.
;
PMMIMD:	 DEFB	YES		;yes=PMMI modem
AUTDIAL: DEFB	NO		;yes=Hayes-type autodial modem
TCHPUL:	 DEFB	'T'		;T=touch, P=pulse (autodial-only)
;
CLOCK:	 DEFB	40		;clock speed in MHz x 10, 25.5 MHz max.
				;2 MHz=20, 3.68 MH=37, 4 MHz=40, etc.
MSPEED:	 DEFB	1		;sets display time for sending a file
				;0=110	1=300  2=450  3=600  4=710
				;5=1200 6=2400 7=4800 8=9600 9=19200
BYTDLY:	 DEFB	5		;0=0 delay  1=10 ms  5=50 ms - 9=90 ms
				;defaut time to send character in ter-
				;minal mode file transfer for slow BBS
CRDLY:	 DEFB	5		;0=0 delay 1=100 ms 5=500 ms - 9=900 ms
				;default time for extra wait after CRLF
				;in terminal mode file transfer
NOFCOL:	 DEFB	5		;number of directory columns
STUPTST: DEFB	NO		;yes=non-PMMI setup routine
SCRNTST: DEFB	NO		;yes=if home cursor and clear screen
				;routine at CLRSCR
RETRY:	 DEFB	YES		;yes=reset the error limit to try again
				;no=abort after 10 consecutive errors
				;(ARPANET users should select yes)
BACKUP:	 DEFB	NO		;yes=make .BAK file
CRCDFLT: DEFB	YES		;yes=default to CRC checking
				;no=default to Checksum checking
TGLECRC: DEFB	YES		;yes=allow toggling of Checksum to CRC
CONVRUB: DEFB	YES		;yes=convert rub to backspace
TGLERUB: DEFB	YES		;yes=allow toggling of rub to backspace
ADDLFD:	 DEFB	NO		;no=no LF after CR to send file in
				;terminal mode (added by remote echo)
TGLELF:	 DEFB	YES		;yes=allow toggling of LF after CR
TRANLOG: DEFB	NO		;yes=allow transmission of logon
				;write logon sequence at location LOGON
SAVCCP:	 DEFB	YES		;yes=do not overwrite CCP
LOCNXT:	 DEFB	NO		;yes=local cmd if EXTCHR precedes
				;no=not local cmd if EXTCHR precedes
TGLELOC: DEFB	YES		;yes=allow toggling of LOCNXT
LSTTST:	 DEFB	YES		;yes=allow toggling of printer on/off
				;in terminal mode. Set to no if using
				;the printer port for the modem
XOFFTST: DEFB	NO		;yes=allow testing of XOFF from remote
				;while sending a file in terminal mode
XONWAIT: DEFB	NO		;yes=wait for XON after sending CR while
				;transmitting a file in terminal mode
TGXOFF:	 DEFB	YES		;yes=allow toggling of XOFF testing
IGNRCTL: DEFB	YES		;yes=do not send control characters
				;above CTL-M to CRT in terminal mode
				;no=send any incoming CTL-char to CRT
EXTRA1:	 DEFB	0		;for future expansion
EXTRA2:	 DEFB	0		;for future expansion
BRKCHR:	 DEFB	'@'-40H		;^@ = Send a 300 ms. break tone
NOCONCT: DEFB	'N'-40H		;^N = Disconnect from phone line
LOGCHR:	 DEFB	'L'-40H		;^L = Send logon
LSTCHR:	 DEFB	'P'-40H		;^P = Toggle printer
UNSAVCH: DEFB	'R'-40H		;^R = Close input text buffer
TRANCHR: DEFB	'T'-40H		;^T = Transmit file to remote
SAVECHR: DEFB	'Y'-40H		;^Y = Open input text buffer
EXTCHR:	 DEFB	'^'-40H		;^^ = Send next character
;
;
; Equates used only by PMMI routines grouped together here
;
PULRATE: DEFB	250		;125=20pps dialing, 250=10pps
CHGBAUD: DEFB	'B'-40H		;^B = Used with PMMIMD in terminal
				;mode to change baud rate on fly
;
;
; Handles in/out ports for data and status
;
I$MDCTL1: IN	A,(MDCTL1)		;in modem control port
	  RET
	  DEFB	0,0,0,0,0,0,0		;spares if needed for non-PMMI
;
O$MDDATP: OUT	(MDDATP),A		;out modem data port
	  RET
	  DEFB	0,0,0,0,0,0,0		;spares if needed for non=PMMI
;
I$MDDATP: IN	A,(MDDATP)		;in modem data port
	  RET
	  DEFB	0,0,0,0,0,0,0		;spares if needed for non-PMMI
;
A$MDRCVB: AND	MDRCVB			;bit to test for receive ready
	  RET
C$MDRCVR: CP	MDRCVR			;value of receive bit when ready
	  RET
A$MDSNDB: AND	MDSNDB			;bit to test for send ready
	  RET
C$MDSNDR: CP	MDSNDR			;value of send bit when ready
	  RET
;
;
;====================== SPECIAL PMMI PORTS =============================
;
I$BAUDRP: IN	A,(BAUDRP)		;in baudrate port
	  RET
O$BAUDRP: OUT	(BAUDRP),A		;out baudrate port
	  RET
O$MDCTL1: OUT	(MDCTL1),A		;out modem control port #1
	  RET
O$MDCTL2: OUT	(MDCTL2),A		;out modem control port #2
	  RET
;
;================== END OF SPECIAL PMMI PORTS ==========================
;
;
LOGONPTR: DEFW	LOGON
J$DIAL:	  JP	DIAL
J$DSCONT: JP	DSCONT
J$GOODBY: JP	GOODBY
J$INITMD: JP	INITMD
J$NWBAU:  JP	NWBAU
J$NPARIT: JP	NPARIT
J$PRITY:  JP	PRITY
J$STUPR:  JP	STUPR
J$SPMEN:  JP	SPMEN
J$SYSVR:  JP	SYSVR
J$BREAK:  JP	SNDBRK
;
;
; Next six lines should not be changed by user overlay as these go to
; specific locations in the main program, not in the overlay.
;
J$ILPRT:  JP	ILPRT
J$INBUF:  JP	INBUF
J$INLNCP: JP	INLNCP
J$INMDM:  JP	INMDM
J$NXTSCR: JP	NXTSCR
J$TIMER:  JP	TIMER
;
;
; Clear sequences are for Televideo, Lear Siegler, etc.  Change to match
; your terminal.  (Heath uses ESC 4AH for clear to end of screen, ESC 45H
; to clear screen.  Lear Siegler and others use ESC 79H for clear to end
; of screen and ESC 3AH to clear screen.)  Room allowed for four bytes.
; (Last zero needed for stopping the string display.  Any extra 0's just
; act as NOP's.)
;
CLREOS:	CALL	ILPRT
	DEFB	ESC,79H,0,0,0
	RET
;.....
;
;
CLRSCR:	CALL	ILPRT
	DEFB	ESC,3AH,0,0,0
	RET
;.....
;
;
;======================= SIGN-ON MESSAGE ==============================
;
; Send version number and date
;
SYSVR:	LD	A,(PMMIMD)	;using the PMMI S-100 modem?
	OR	A
	JP	Z,SYSVR1	;go if not
	CALL	ILPRT
	DEFB	'Version for PMMI S-100 modem starting at port: ',0
	LD	A,(I$MDCTL1+1)
	CALL	HEXO		;put in PMMI control port number
	CALL	ILPRT
	DEFB	'H',CR,LF,0
	RET
;.....
;
;
SYSVR1:	CALL	ILPRT		;if not using the PMMI S-100 board
	DEFB	'Version for Non-PMMI modem',CR,LF,0
	RET
;.....
;
;
;==================== LOGON MESSAGE (IF ANY) ===========================
;
; Insert your logon message here.  End with a 0 (for"CALL ILPRT").
; PMMIusers have 59 bytes available, non-PMMI users have approximately
; 2K bytes available as they can overwrite all the following PMMI rou-
; tines if they wish.  This method allows the external overlays to have
; plenty of room.  It keeps the phone number library at a fixed location.
;
LOGON:	DEFS	59		;up to 59 characters allowed
	DEFB	0		;to terminate the logon message
;.....
;
;
;=============== NON-PMMI INITIALIZATION (IF ANY) ======================
;
; Insert your initialization routine here if needed.  Can replace the
; following special PMMI area to set speed and auto-dial.  Over 950
; bytes are available for this purpose.  (End your routine with a RET.)
;
;
INITMD:	RET
;
;========== NON-PMMI SETUP (SPEED CHANGE, ETC.) IF ANY ==============
;
; Insert your speed change and/or auto-dialing routines here.  Over 950
; bytes are available (INCLUDING INITMD, above).  End your routine with
; a RET.
;
STUPR:	RET
;
;
; Not needed if using the PMMI board, as it has its own break routine
;
SNDBRK:	RET
;
;
;**************** START OF SPECIAL PMMI ROUTINES **********************
;
;
;=======================================================================
;
;			SETS THE BAUD RATE
;
;=======================================================================
;
;
STBAUD:	LD	A,(ANSWFLG)	;if 'O' or 'A' not requested and
	OR	A		;   baudrate not specified, returns
	JP	Z,FXBAUD	;   with current mode and rate
	LD	A,(ORIGFLG)	;if option requested, a blank returns
	OR	A		;   with current mode and rate
	RET	NZ		;no change if neither 'O' or 'A' shown
;
FXBAUD:	CALL	GTBAUD		;calculate PMMI baud rate divisor
	CALL	STMSPD		;set the file time transfer value
	CALL	O$BAUDRP	;set the PMMI board to that baudrate
	CP	52
	LD	A,5FH		;DTR (filter for over 300 baud)
	JP	C,GT300		;yes, greater than
	LD	A,7FH		;DTR (filter for 300 and less baud)
;
GT300:	CALL	O$MDCTL2
	LD	(MDCTLB),A	;save modem control byte
;
OFHOOK:	LD	HL,7500		;throw in some delay
;
OFFDLY:	DEC	L
	JP	NZ,OFFDLY
	DEC	H
	JP	NZ,OFFDLY
	LD	A,(UARTCT)	;UART control byte for 'A' or 'O'
	CALL	O$MDCTL1	;now set to answer or originate
	LD	A,C
	LD	(MSPEED),A	;set the file transfer time value
	XOR	A		;clear the flags
	RET
;.....
;
;
;=======================================================================
;		 CALCULATES THE BAUD RATE DIVISOR
;
; Returns with current baud rate intact if a blank or null in the speed
; field (extent area).
;
GTBAUD:	LD	A,(FCB+9)	;get 1st digit of requested baudrate
	CP	' '		;if a space, return with current speed
	LD	A,(CURRENT)
	RET	Z
	LD	A,(FCB+9)
	OR	A		;if a null, return with current speed
	LD	A,(CURRENT)
	RET	Z
;
	LD	DE,FCB+9	;get the requested speed
	LD	HL,0
;
DECLP:	LD	A,(DE)		;get the ASCII digit
	INC	DE
	CP	' '
	JP	Z,DECLP
	CP	'0'		;numerals are 0-9
	JP	C,BADRTE
	CP	'9'+1
	JP	NC,BADRTE
	SUB	'0'
	LD	B,H
	LD	C,L
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,BC
	ADD	HL,HL
	ADD	A,L
	LD	L,A
	JP	NZ,DIGNC
	INC	H
;
DIGNC:	LD	A,E
	CP	FCB+12
	JP	NZ,DECLP
	LD	A,H
	CPL
	LD	D,A
	LD	A,L
	CPL
	LD	E,A
	INC	DE
	LD	HL,15625	;250000/16
	LD	BC,-1
;
DIVLP:	INC	BC
	ADD	HL,DE
	JP	C,DIVLP
	LD	A,B
	OR	A
	LD	A,C
	LD	(CURRENT),A	;can use this the next time by default
	RET	Z
;
BADRTE:	CALL	ERXIT
	DEFB	'++ INVALID BAUDRATE ++$'
;.....
;
;
;=======================================================================
;		    SETS 'MSPEED' TO BAUD RATE
;
;
STMSPD:	LD	C,0		;changes PMMI mspeed for 110-710 bps
	CP	100		;<300 bps
	RET	NC
	INC	C		;C=1 for 300 bps
	CP	40		;<450 bps
	RET	NC
	INC	C		;C=2 for 450 bps
	CP	30		;<600 bps
	RET	NC
	INC	C		;C=3 for 600 bps
	CP	24		;<710 bps
	RET	NC
	INC	C		;C=4 for 710 bps
	RET
;.....
;
;
; Change baudrate on-the-fly with CTL-B (while in terminal mode)
;
NWBAU:	LD	A,(PMMIMD)
	OR	A
	RET	Z
	CALL	ILPRT
	DEFB	CR,LF,'Enter new Baudrate: ',0
	LD	HL,FCB+9
	LD	(HL),' '	;keep current baud if none included
;
NWBAU1:	CALL	KEYIN		;get the baud rate
	CP	CR		;carriage ret finishes baud rate entry
	CALL	Z,CRLF		;if a 'CR', baud rate has been entered
	JP	Z,FXBAUD	;go change the baud rate
;.....
;
;
NWBAU2:	CP	'0'		;numerals are 0-9
	JP	C,NWBAU1
	CP	'9'+1
	JP	NC,NWBAU1	;if not a numeral, ignore, ask again
	LD	(HL),A		;store answer starting at FCB+9
	CALL	TYPE		;show the numeral on the CRT
	INC	HL		;next storage location in FCB
	JP	NWBAU1		;get the next numeral
;.....
;
;
;======================= PRITY ROUTINES ===============================
;
;--->PRITY: Routine to setup PMMI for odd/even parity.
;
PRITY:	LD	A,(PMMIMD)	;is modem a PMMI?
	OR	A		;set flags
	RET	Z		;no, return
	LD	A,(OPRITY)	;get odd parity request byte
	OR	A		;set flags
	JP	NZ,EVNPAR	;if not odd see if it is even
	LD	A,(UARTCT)	;get uart/modem control byte
	AND	ODPAMSK
	JP	PRITY1
;...
;
;
EVNPAR:	LD	A,(EPRITY)	;get even parity request byte
	OR	A		;set flags
	RET	NZ		;if even parity not specified return
	LD	A,(UARTCT)	;get uart/modem control byte
	AND	ODPAMSK		;set for parity
	OR	EVPAMSK		;now set for even parity
;
PRITY1:	LD	(UARTCT),A
	JP	O$MDCTL1	;send to PMMI
;.....
;
;
NPARIT:	LD	A,(PMMIMD)
	OR	A
	RET	Z
	LD	A,(UARTCT)
	OR	NOPAMSK		;reset parity bit on PMMI
	JP	O$MDCTL1
;.....
;
;
;==================== END OF PRITY ROUTINES ===========================
;
;
;=======================================================================
;
;		   HAYES/PMMI DIALING ROUTINES
;
;=======================================================================
;
;
	DEFS	128		;for expansion
;
; Modem control command words
;
BRKMSK	EQU	0		;tele line on hook (break while dialing)
CLEAR	EQU	3FH		;idle mode
DTMSK	EQU	1		;dial tone mask
MAKEM	EQU	1		;tele line make (off hook)
RBLMT	EQU	35		;7 seconds to wait til no-ring-heard msg
RBWAIT	EQU	50		;5 second delay before redialing PMMI
SMWAIT	EQU	15		;1.5 sec delay before redialing HAYES
TMPUL	EQU	80H		;timer pulses mask bit
TRATE	EQU	250		;value for 0.1 second
;.....
;
;
; Dialing routine
;
DIAL:	LD	A,(PMMIMD)	;using a PMMI modem?
	OR	A
	JP	NZ,DIAL1
	LD	A,(AUTDIAL)
	OR	A
	RET	Z		;return if neither modem
	CALL	SMNSY		;make sure autodial modem speaker is on
;
DIAL1:	XOR	A
	LD	(AUTDIR),A	;zero the direct to terminal mode flag
	LD	(AUTOFL),A	;zero the auto-linking flag
	LD	(CRFLAG),A	;zero the continuous dial flag
	LD	HL,0
	LD	(DIALCT),HL	;zero the dial count
	LD	HL,CMDBUF+1	;point to the number of characters in
	LD	A,(HL)		;  the buffer, then get the number
	CP	3+1		;anything typed after 'CAL'?
	JP	C,DIAL2		;if not, go through library routine
;
;
; If there were only 3 characters, then "CAL<RET>" was typed -- the user
; obviously expecting to get a phone number (or letter) from the library
; file.  If 4 or more, a number (or letter) was typed in from the menu
; command line, so move the characters down 4 to compensate.  Needed for
; auto-redialing of menu command line entries.
;
	LD	C,A		;put into the 'C' reg.
	LD	B,0		;will move original number down 4
	SUB	4		;eliminate the 'CAL' portion
	LD	(HL),A		;store new count at cmdbuf+1
	INC	HL		;CMDBUF+2 (first character of string)
	EX	DE,HL		;'DE' now has CMDBUF+2
	LD	HL,CMDBUF+6	;point to number (or letter) to dial
	CALL	MOVER		;move the group down 4 places
	JP	DIAL4		;check if library number, then dial
;...
;
;
; Comes here if no phone number was manually entered after 'CAL' and if
; no phone library code was entered.  Displays the phone number library
; then asks for an entry.
;
DIAL2:	LD	C,18		;number of lines to move
	LD	HL,NUMLIB	;start of phone number library
	LD	DE,BUFFER	;buffer add. to store them temporarily
	CALL	NEWLINE		;start with CR/LF
	LD	(DE),A		;+LF
	INC	DE		;and bump it
;
DIAL3:	LD	B,LIBLEN	;number of bytes to move
	CALL	MOVE		;move to buffer
	CALL	SPACES		;2 entries + 3 spaces = 71 characters
	PUSH	HL		;save source address
	PUSH	DE		;save destination address
	LD	DE,17*LIBLEN	;get offset of 17 times entry length
	ADD	HL,DE		;add it to source address
	POP	DE		;restore destination address
	LD	B,LIBLEN	;get length of library entry
	CALL	MOVE		;move another entry
	POP	HL		;restore source address
	CALL	NEWLINE
	DEC	C		;one less line to print
	JP	NZ,DIAL3	;if not zero, print another
	LD	A,'$'		;BDOS print routine terminate character
	LD	(DE),A		;store in buffer
	CALL	CLRTST
	LD	C,PRINT
	LD	DE,BUFFER	;print the library on the CRT
	CALL	BDOS
	CALL	J$ILPRT		;ask which one is wanted
	DEFB	CR,LF,'Enter library code or phone number,',CR,LF
	DEFB	'Hit RET to abort this function now or',CR,LF
	DEFB	'CTL-X quits while dialing or ringing: ',0
	LD	DE,CMDBUF
	CALL	INBUF		;get the answer from the keyboard
;
;
; You now have either a library code or a manually entered phone num-
; ber.	These either came from the menu command line or from the library
; command line.  Next we see if a code, if so, get the corresponding
; line with phone number from the library.  If a number greater than
; one digit, we ignore the library look-up.  (Ringback numbers must end
; with letter 'R'.)
;
DIAL4:	LD	HL,CMDBUF+1	;number of characters in buffer
	LD	A,(HL)
	OR	A		;null means CR was typed
	JP	Z,DLXIT2	;abort dialing, return to menu
	LD	(NUMBER),A
	LD	A,(CMDBUF+3)	;see if at least two characters entered
	CP	'/'		;slash for linking, direct to terminal
	CALL	Z,AUTO		;  mode on answer
	CP	','		;comma used for linking
	CALL	Z,AUTO1		;if yes, set it up for auto-linking
;
;
; Check to see how many characters were typed.	If more than one, then
; it was a hand-entered phone number, so exit.
;
DIAL5:	CALL	DIALBG		;disconnect, reconnect
	LD	A,(AUTOFL)	;auto-link flag set?
	OR	A
	JP	NZ,AUTO2	;if yes exit
	LD	A,(NUMBER)	;number of characters in buffer
	LD	(CMDBUF+1),A	;reset the character count, if needed
	CP	1+1		;more than one character?
	JP	NC,DIAL14	;if more than one, hand-entered number
	LD	HL,CMDBUF+2	;first character in phone number line
;
;
;
; If just one character entered, see if a (A-Z) letter
;
DIAL6:	LD	A,(HL)
	LD	B,'A'		;first letter of alphabet
	LD	E,0		;counts number of letters to match
	LD	C,26		;number of letters in alphabet
;
DIAL7:	CP	B		;letter from table?
	JP	Z,DIAL9		;if yes, get phone number, else
	INC	B		;make next letter (A-Z)
	INC	E		;count up
	DEC	C		;count down
	JP	NZ,DIAL7	;try next one in (A-Z) table
;
;
; If not (A-Z) then should be (0-9)
;
	LD	B,'0'		;first digit to check
	LD	E,26		;point past alpha codes
	LD	C,10		;number of digits in table
;
DIAL8:	CP	B		;number from table?
	JP	Z,DIAL9		;if yes, go dial, else
	INC	B		;make next digit to compare
	INC	E		;make next table line number
	DEC	C		;count down - loop counter
	JP	NZ,DIAL8	;loop
	JP	DIALBD		;error if not a number or a letter
;
;
; Now have a match between the requested code and one in the library.
; E-reg. holds the library line number (1-36) that matches the requested
; code (A-Z or 0-9).
;
DIAL9:	LD	HL,NUMLIB	;phone number library
	LD	BC,LIBLEN	;length of library entry
	LD	A,E		;number of times to library length to HL
	OR	A		;set flags
	JP	Z,DIAL11
;
DIAL10:	LD	A,(HL)		;get first char of selected lib entry
	OR	A		;set flags
	JP	Z,DIALBD	;send bad library msg and abort
	ADD	HL,BC		;increment 'HL' by library length
	DEC	E		;countdown
	JP	NZ,DIAL10	;not there yet, loop
;
;
; Now have the line in the phone number library matching the requested
; letter so store that line starting at 'CMDBUF+1'
;
DIAL11:	LD	B,LIBLEN	;number of characters to get from table
	LD	DE,CMDBUF+1	;point to buffer
	EX	DE,HL		;'HL' points to CMDBUF+1
	LD	(HL),B		;length of each table entry
	EX	DE,HL		;restore the registers
	INC	DE		;point to first char position in buffer
	CALL	MOVE		;move the table entry to the buffer
;
;
; Now have the full line including phone number in 'CMDBUF' area.  Scan
; past the descriptive portion of library entry - terminate scan at the
; first '.'  This allows commas and numbers to be part of the text, such
; as:
;	     'A=DataTech, Node 7..1-408-238-9621'
;
DIAL12:	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	CALL	NZ,SMINIT	;if yes, initialize
	LD	HL,CMDBUF+1
	LD	E,(HL)		;number of chars in buffer
	INC	HL		;point to 1st character in buffer
;
DIAL13:	LD	A,(HL)		;get next character
	CALL	TYPE		;show it
	INC	HL		;bump pointer
	DEC	E		;decrement count
	JP	Z,DLXIT		;exit if no '.' (bad library entry)
	CP	'.'		;dot?
	JP	Z,DIAL15	;yes, go dial the phone
	JP	DIAL13		;no, loop for next character
;.....
;
;
; There is a user entered phone number in 'CMDBUF' area
;
DIAL14:	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	CALL	NZ,SMINIT	;if yes, initialize
	LD	HL,CMDBUF+1	;get the number of characters in buffer
	LD	A,(HL)
	LD	E,(HL)
	INC	HL		;point to 1st character to dial
;.....
;
;
; Loop to dial the phone number pointed to by 'HL', character count in
; the 'E' register.
;
DIAL15:	LD	A,(HL)		;get first number from the buffer
	OR	A		;set flags
	JP	Z,DIALBD	;bad number if a null
;
;
; Dial a digit, check keyboard for abort
;
	CALL	DL		;dial a digit, show on CRT
	CALL	STAT		;keypress?
	JP	Z,DIAL17	;if not, exit
	CALL	KEYIN		;yes, go get it
	CP	CANCEL		;CTL-X?
	JP	NZ,DIAL17	;if not, exit
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,DLXIT		;if not, exit now, otherise clear line
;
;
; If using an autodial modem, backspace 30 tims to make sure the entire
; number plus 'DT' part of 'ATDT' is erased.
;
	LD	C,30
;
DIAL16:	LD	B,BKSP
	CALL	SNDCHR		;send to the modem to cancel call
	DEC	C
	JP	NZ,DIAL16	;if not zero, do another
	LD	B,CR
	CALL	SNDCHR
	LD	A,' '
	CALL	TYPE		;show on CRT
	JP	DLXIT		;now go abort
;
DIAL17:	INC	HL		;bump pointer
	DEC	E		;one less character to go
	JP	NZ,DIAL15	;if not done, send the next digit
;
;
; Show the number of dial attempts
;
	CALL	J$ILPRT
	DEFB	' - try #',0
	LD	HL,(DIALCT)	;increment the dial count
	INC	HL
	LD	(DIALCT),HL
	CALL	DECOUT		;show number of attempts so far
	LD	A,' '		;extra space to position cursor
	CALL	TYPE
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,DIAL19	;if not, exit
	LD	B,CR		;tells the modem the number is done
	CALL	SNDCHR		;just have one character to send
;
DIAL18:	CALL	J$INMDM		;catch any output from the modem
	JP	NC,DIAL18
	JP	SMRSLT		;number sent to modem, now get results
;
;
; Dialing is all done, this section is PMMI-only
;
DIAL19:	LD	A,07FH		;turn on PMMI 'DTR'
	CALL	O$MDCTL2	;timer rate?
	LD	B,1		;0.1 second per interval
	CALL	J$TIMER
	LD	A,5DH		;2 stop bits, nor parity, 8 data bits
	CALL	O$MDCTL1
	LD	D,4		;clear to send mask
	LD	C,WAITCTS	;wait time for CTS
	CALL	WAIT		;(30 seconds, can set 'WAITCTS' for
				;up to 51 seconds for European use)
;
; If PMMI connection made, go get options for starting communications
;
	JP	NC,CONMD	;connection made
;
; Connection not made, see if a redial is desired
;
;	CALL	DSCONT		;hang-up so we can redial
;
DLGN:	LD	SP,STACK	;reset the stack to normal, just in case
	LD	A,(CRFLAG)	;continuous redial flag
	OR	A
	JP	NZ,DLGN2	;if already set, go dial again
	CALL	J$ILPRT		;see if we should keep trying
	DEFB	CR,LF,CR,LF,'     Redial? (C/Y/N/Q): ',BELL,0
	CALL	KBDCHR
	CALL	CRLF		;turn up a line
	CP	'Y'		;redial?
	JP	Z,DLGN2		;yes, redial
	CP	'C'		;continuous redial?
	JP	Z,DLGN1		;if yes, set continuous redial flag
	CP	'Q'
	JP	NZ,DLXIT1	;none of these, quit
	CALL	SMQT		;turn off the loud speaker for 'Quiet'
;
DLGN1:	LD	A,1
	LD	(CRFLAG),A	;continuous redial flag
;
DLGN2:	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	NZ,DLGN3	;if yes, exit
	LD	B,RBWAIT	;wait for PMMI reset (or busy)
	CALL	J$TIMER		;or busy tone may be sensed as dialtone
	JP	DLGN4
;
DLGN3:	LD	B,SMWAIT
	CALL	J$TIMER
;
DLGN4:	CALL	CRLF		;start a new line
	JP	DIAL5		;redial entry point
;.....
;
;
; Connection has been made
;
CONMD:	LD	A,(PMMIMD)
	OR	A
	JP	Z,CONMD1
	LD	A,(CURRENT)	;get curret baud rate
	CALL	O$BAUDRP	;set baudrate
;
CONMD1:	LD	B,2
	CALL	J$TIMER
	CALL	J$ILPRT
	DEFB	BELL,CR,LF,CR,LF,'     CONNECTED',0
	LD	A,(AUTDIR)	;going direct to terminal mode?
	OR	A
	JP	NZ,RETRN
	LD	A,(CRFLAG)	;in continuous redial or first time try?
	OR	A
	JP	Z,RETRN		;go to terminal mode if first time
	CALL	J$ILPRT
	DEFB	' - any key for terminal mode ',0
;
CONMD2:	LD	E,10
;
CONMD3:	CALL	STAT		;keypress?
	JP	Z,CONMD4	;exit if no keys pressed
	CALL	KEYIN
	XOR	A
	JP	RETRN		;key pressed, go to terminal mode
;
CONMD4:	LD	B,1		;wait 0.1 second
	CALL	J$TIMER
	DEC	E		;one less loop to make
	JP	NZ,CONMD3	;see if a keyboard character yet
	LD	A,BELL		;sound a bell
	CALL	TYPE
	JP	CONMD2		;reset the counter
;.....
;
;
; Automatic dialing routine, prints the number being dialed.  If we find
; 'R', it either has to be the final character for ringback or toss it.
;
DL:	CALL	TYPE		;print whatever character, dashes, etc.
	CP	'R'		;could it be a ringback character?
	JP	NZ,DL1		;if not, probably a number so exit
	LD	A,E		;get the character count.  Is this "R"
	CP	1		;  the last character in the string?
	JP	Z,RNGBK		;if yes, set up ringback
	RET			;if not, ignore the 'R'
;
DL1:	LD	B,A		;store the character for now
	CALL	DLD		;check for alternate dialing like 'MCI'
	LD	A,B		;get the original character back
;
DL2:	CP	'*'		;* is a valid dial digit
	JP	Z,DL3
	CP	'#'		;# is a valid dial digit
	JP	Z,DL3
	CP	','		;comma indicates a short delay-time
	JP	Z,DL3
	CP	'0'		;digits are (0-9)
	RET	C		;exit less than ASCII '0'
	CP	'9'+1
	RET	NC		;exit if more than ASCII '9'
	SUB	'0'		;strip ASCII - could also do 'ANI 0FH'
	JP	NZ,DL3
	LD	A,10		;convert zero to 10 pulses
;
;
; Sends the digit to the modem.  Waits 100 ms. after each digit to in-
; sure it gets to the modem ok.
;
DL3:	LD	C,A
	LD	A,(PMMIMD)	;using a PMMI?
	OR	A
	JP	NZ,DL4		;if yes, exit
	CALL	SNDCHR		;character is already in the 'B' reg.
	LD	B,1		;slight delay to let modem settle down
	JP	J$TIMER
;...
;
;
DL4:	LD	A,(PULRATE)
	CALL	O$BAUDRP
;
DL5:	CALL	I$BAUDRP
	AND	TMPUL
	JP	NZ,DL5
;
DL6:	CALL	I$BAUDRP
	AND	TMPUL
	JP	Z,DL6
;
DL7:	LD	A,MAKEM
	CALL	O$MDCTL1
;
DL8:	CALL	I$BAUDRP
	AND	TMPUL
	JP	NZ,DL8
	LD	A,BRKMSK
	CALL	O$MDCTL1
;
DL9:	CALL	I$BAUDRP
	AND	TMPUL
	JP	Z,DL9
	DEC	C
	JP	NZ,DL7
	LD	A,MAKEM
	CALL	O$MDCTL1
	LD	B,2
	JP	J$TIMER
;.....
;
;
; Print bad library number message and abort if a null is encountered.
;
DIALBD:	CALL	J$ILPRT
	DEFB	CR,LF,CR,LF,'++ Bad library number called ++',CR,LF,0
;
DLXIT:	CALL	CRLF		;turn up a new line
;
DLXIT1:	LD	SP,STACK	;make sure the stack is normal again
;;;	CALL	J$GOODBY	;user routine to disable DTR, if any
	DEFB	0,0,0		;(PREVENT DOUBLE TIME FOR DISCONNECT)
	CALL	J$DSCONT	;hang up the phone and reset the modem
;
DLXIT2:	XOR	A
	LD	(CRFLAG),A	;reset the continuous redial flag
	JP	MENU
;.....
;
;
; Disconnect from the line, reconnect and wait for the dialtone.
;
DIALBG:	LD	A,(AUTDIAL)	;Hayes-type autodial modem?
	OR	A
	RET	NZ		;if yes, finished
	LD	A,MAKEM		;go off-hook
	CALL	O$MDCTL1
	LD	D,DTMSK		;dial tone mask
	LD	C,50		;waits up to 10 seconds for dial tone
	CALL	WAIT		;wait for dial tone
;
;
; Wait subroutine will return with carry set if unable to get dialtone.
; If carry is not set, the dialtone was received.
;
	RET	NC		;if dial tone within 10 seconds
	CALL	J$ILPRT		;otherwise print error message
	DEFB	CR,LF,CR,LF,'++ NO DIAL TONE ++ ',BELL,0
	POP	HL		;restore the stack to normal
	JP	DLXIT		;forget it.
;.....
;
;
; Do any alternate dialing such as 'MCI' or 'SPRINT'
;
DLD:	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	RET	Z		;if not, exit
	LD	A,(TCHPUL)	;using touch tone dialing?
	CP	'T'
	RET	NZ		;if not, ignore
	LD	A,B		;get the character back
	CP	'<'		;alternate dialing system #1 (MCI?)
	JP	NZ,DLD1		;if not, exit
	PUSH	HL		;save the current values
	LD	HL,ALTDL1	;alternate dialing area
	JP	DLD2
;
DLD1:	CP	'>'		;alternate dialing system #2 (Sprint?)
	RET	NZ		;if neither, exit
	PUSH	HL		;save the current values
	LD	HL,ALTDL2
;
DLD2:	LD	A,(HL)
	CP	'$'		;ready to terminate?
	JP	Z,DLD3		;if yes, exit
;
;
; Move the semicolons up one line if you do not want to see the Sprint
; number dialed.
;
	CALL	TYPE		;display the character
;;;	DB	0,0,0		;(keeps the total bytes similar)
	LD	B,A		;need the char. in 'B' to send to modem
	CALL	DL2		;send proper characters to the modem
	INC	HL		;next location
	CALL	STAT		;keypress?
	JP	Z,DLD2		;if not, do the next character
	CALL	KEYIN		;yes, go get it
	CP	CANCEL		;CTL-X?
	JP	NZ,DLD2		;if not, handle the next character
	POP	HL		;if yes, reset the stack
	JP	DLXIT		;if yes, exit
;
DLD3:	LD	A,' '
	LD	B,A		;clears 'B' from last digit sent
	CALL	TYPE
	POP	HL		;restore the stack
	RET
;.....
;
;
; Disconnect the autodial modem from the phone line.  Sends 'I, CR' to
; the Racal-Vadic to return to IDLE mode
;
GOODBY:
DSCONT:	LD	A,(AUTDIAL)	;using a Hayes-type autodial modem?
	OR	A
	JP	NZ,DSCON1	;if yes, skip PMMI section
	XOR	A
	CALL	O$MDCTL1	;hang up
	CALL	O$MDCTL2	;clear DAV, ESD, etc.
	PUSH	BC
	LD	B,10		;1 second for PMMI to disconnect
	CALL	J$TIMER
	POP	BC
	RET
;...
;
;
; Disconnect the autodial modem from the phone line
;
DSCON1:	LD	B,12		;1.2 seconds pause
	CALL	J$TIMER
	LD	HL,SM$DISC	;get into command mode
	CALL	SNDOUT
	LD	B,12		;another 1.2 seconds pause
	CALL	J$TIMER
	LD	A,' '		;space character
;
;
; If printing +++ and ATH, ATD, etc. move the three semicolons up one
; line to include a space on the CRT to look better.
;
	DEFB	0,0,0
;;;	CALL	TYPE		;show on local CRT only
	LD	HL,SM$DISC1	;now disconnect the modem
	CALL	SNDOUT
;
DSCON2:	CALL	J$INMDM		;wait 0.1 seconds after last character
	JP	NC,DSCON2
	RET
;.....
;
;
;-----------------------------------------------------------------------
;			   Hayes Stuff
;
SMQT:	LD	A,(SPKRFLG)	;speaker flag set to quiet?
	OR	A
	RET	NZ		;if yes, forget it
	LD	A,YES
	LD	(SPKRFLG),A	;flip the flag to quiet, now
	LD	HL,SM$SOFF
	CALL	SNDOUT
	LD	B,6
	JP	J$TIMER		;time for an 'OK' from modem and return
;.....
;
;
SMNSY:	LD	A,(SPKRFLG)	;speaker already turned on?
	OR	A
	RET	Z		;if yes, forget it
	LD	A,NO		;set for noisey, now
	LD	(SPKRFLG),A
	LD	HL,SM$SON
	CALL	SNDOUT
	LD	B,6
	JP	J$TIMER
;.....
;
;
; Hayes-like autodial modem control codes
;
SM$DIAL:  DEFB	'ATDT $'	;set for touch (or pulse) dialing
SM$DISC:  DEFB	'+++$'		;puts the modem in local command mode
SM$DISC1: DEFB	'ATH',CR,'$'	;disconnects the modem
SM$SOFF:  DEFB	'ATM0',CR,'$'	;turns the speaker off
SM$SON:	  DEFB	'ATM1',CR,'$'	;turns the speaker on
SPKRFLG:  DEFB	0		;0 = speaker has not been silenced
;.....
;
;
; Set the autodial modem for pulse dialing
;
SMINIT:	LD	A,(TCHPUL)	;touch or pulse dialing for autodial?
	LD	(SM$DIAL+3),A	;store
	LD	HL,SM$DIAL
	CALL	SNDOUT
;
SMINT1:	CALL	J$INMDM		;wait for modem to finish, if needed
	JP	NC,SMINT1
	RET
;.....
;
;
; Send the string pointed to by 'HL' to both the CRT and the modem
;
SNDOUT:	CALL	SNDNOW		;wait until modem is ready
	LD	A,(HL)		;get the character
	CP	'$'
	RET	Z		;if yes, finished
	LD	A,(HL)
	CALL	O$MDDATP	;send to modem
;
;
; If you want to print the +++ ATD, etc. from Hayes-type units, move the
; three semi-colons down one line.
;
;;;	CALL	TYPE		;show on CRT
	DEFB	0,0,0		;(PREVENT SHOWING THE +++ ATD)
	INC	HL
	JP	SNDOUT
;.....
;
;
; Checks for answer from Hayes-type autodial modem
;
;
SMRSLT:	CALL	RCVRDY		;see if any incoming character yet
	JP	Z,SMRSL1	;if yes, exit and look at it
	CALL	STAT		;else see if want to abort ringing
	JP	Z,SMRSLT	;if neither, wait for one of them
	CALL	KEYIN		;get character from keyboard
	CP	CANCEL		;CTL-X to terminate dialing?
	JP	NZ,SMRSLT	;if not, keep going
	LD	B,CR
	CALL	SNDCHR		;tells the modem to hang up right away
	JP	DLXIT		;abort dialing routine
;
SMRSL1:	CALL	I$MDDATP	;get the character, then
	AND	7FH		;remove any parity
	LD	B,A		;store for 'GIVLF' area if needed
	CP	'B'		;'BUSY' (for Anchor modems, etc.)
	JP	Z,BUSY		;if busy, flush string and retry
	CP	'0'		;'OK' single digit result code
	JP	Z,SMRSL1	;ok, loop for next response
	CP	'O'		;'OK' verbose digit result coe
	JP	Z,SMRSL1	;ok, loop for next response
	CP	'1'		;'CONNECT', single digit result code
	JP	Z,ON$LIN	;connected, reset redial flags
	CP	'C'		;'CONNECT', verbose result code
	JP	Z,ON$LIN	;connected, reset redial flags
	CP	'3'		;'NO CARRIER', single digit result code
	JP	Z,NO$CAR	;no carrier, flush string and retry
	CP	'N'		;'NO CARRIER', verbose result code
	JP	Z,NO$CAR	;no carrier, flush string and retry
	CP	'4'		;'ERROR', single digit result code
	JP	Z,FAILED	;error, go display
	CP	'E'		;'ERROR', verbose result code
	JP	Z,FAILED	;error, go display
	CP	'5'		;'CONNECT 1200' single digit result code
	JP	Z,ON$120	;connected, reset redial flags
;
SMDM1:	CP	LF		;<LF> is end-of-line for verbose mode
	JP	Z,SMRSLT	;yes, go get the next response
	CP	CR		;<CR> may precede digit in digit mode
	JP	Z,SMRSLT	;yes, go get the next response
;
	CALL	STAT		;else, see if want to abort ringing
	JP	Z,SMDM1A	;if not, get next character
	CALL	KEYIN		;else, get character from keyboard
	CP	CANCEL		;CTL-X to terminate dialing?
	JP	NZ,SMDM1A	;if not, keep going
	LD	B,CR
	CALL	SNDCHR		;tells the modem to hang up right away
	JP	DLXIT		;abort dialing routine
;
SMDM1A:	CALL	J$INMDM		;get next character
	JP	SMDM1		;loop until end of response encountered
;
;
; The Ancho modem gives a busy result code, although still waits the
; normal time-out period to do it.
;
BUSY:	CALL	J$ILPRT
	DEFB	'busy! ',0
	JP	DLGN
;.....
;
;
; Failed call is usually caused by continuous ringing with no answer.
; The modem times out (can be set to either 30 seconds or 60 seconds.)
;
FAILED:	CALL	J$ILPRT
	DEFB	'abort ',0
	JP	DLGN
;.....
;
;
NO$CAR:	CALL	J$ILPRT
	DEFB	'no carrier ',0
	JP	DLGN
;.....
;
;
ON$LIN:	CALL	J$ILPRT
	DEFB	'on line',0
	JP	CONMD
;.....
;
;
ON$120:	CALL	J$ILPRT
	DEFB	'on at 1200',0
	JP	CONMD
;.....
;
;		end of special Hayes-like handling
;-----------------------------------------------------------------------
;
; Handles the special ringback numbers.  Dials, lets it ring only once,
; hangs up and then redials.
;
RNGBK:	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	NZ,RNGBK2	;if yes, ringback not possible
	LD	A,(CMDBUF+1)	;get the number of chars. in the buffer
	DEC	A		;subtract 1 to avoid the ringback char
	LD	(CMDBUF+1),A	;store the new value
	LD	D,DTMSK		;load tone detect mask
	LD	C,RBLMT		;waits up to 7 seconds for a ring
	CALL	WAIT
	JP	NC,RNGBK0	;if no ring detected, pretend we got one
	JP	RNGBK1		;hangup, redial, & listen for carrier
;
RNGBK0:	LD	B,25		;got a ring, wait 2.5 seconds
	CALL	TIMER
	CALL	I$BAUDRP	;is tone still present?
	AND	D
	JP	Z,DLGN		;yes, must be busy, do a normal redial
;
;
; Hang up, redial and listen for dial tone
;
RNGBK1:	CALL	HANGUP		;hang up the phone
	CALL	J$ILPRT
	DEFB	'ringback set, first ring ',0
	LD	B,RBWAIT	;wait 5 seconds before redialing
	CALL	J$TIMER		;  for line to clear, etc.
	CALL	DIALBG		;disconnect, reconnect, wait for tone
	JP	C,DLXIT
	LD	A,(NUMBER)	;number of characters in buffer
	CP	1+1		;more than one character?
	JP	NC,DIAL14	;if more than one, hand-entered number
	JP	DIAL12		;go redial for the table ringback number
;.....
;
;
RNGBK2:	CALL	J$ILPRT
	DEFB	CR,LF,'++ No ringback for autodial modem ++',0
	POP	HL		;reset the stack
	JP	DLXIT
;.....
;
;
HANGUP:	LD	A,CLEAR
	CALL	O$MDCTL2
	XOR	A
	JP	O$MDCTL1	;turn off DTR, originate/answer tones
;.....
;
;
; This is the auto-linking area.  Up to 32 numbers may be linked, each
; should have a comma for a separator, such as:
;
;	B>>COMMAND: CAL A,F,3,A,G,A,H
;
AUTO:	LD	(AUTDIR),A	;direct to terminal mode on answer
;
AUTO1:	LD	A,0FFH		;set the flags to -1
	LD	(AUTOFL),A	;set the auto-linking flag
	LD	(CRFLAG),A	;set the continuous redial flag
	LD	B,64		;maximum number of characters to move
	LD	HL,CMDBUF+1	;start with number in the string
	LD	DE,CMDBUF+65	;move to aft part of buffer
	JP	MOVE		;when finished return to caller
;.....
;
;
; Linking routine
;
AUTO2:	LD	A,(AUTOFL)	;increment the flag for each new try
	INC	A
	INC	A
	LD	(AUTOFL),A
	LD	C,A		;hold momentarily
	LD	B,0
	LD	A,(CMDBUF+65)	;see how many characters typed
	CP	C
	JP	NC,AUTO3
	LD	A,1		;reset the flag to start over
	LD	C,A
	LD	(AUTOFL),A
;
AUTO3:	LD	HL,CMDBUF+65
	ADD	HL,BC
	JP	DIAL6		;go to work
;.....
;
;
AUTDIR:	DEFB	0		;direct to terminal mode on answer
AUTOFL:	DEFB	0		;auto-linking flag
NUMBER:	DEFB	0		;number of characters in CMDBUF
;.....
;
;
; Time-out routine.  Must be called with mask in 'D' reg. for input at
; relative port 2 and number of seconds (times 10) in 'C' reg.
;
WAIT:	LD	B,2
	CALL	TIMER		;wait for timer to go high then low
	CALL	I$BAUDRP	;PMMIADDR+2 (modem status port)
	AND	D		;(CTS or dialtone mask)
	RET	Z		;active low, so return on 0
	PUSH	BC		;save the registers
	PUSH	DE
	CALL	STAT		;keypress?
	JP	Z,WAIT1		;if not, exit
	CALL	KEYIN		;yes, get char
	CP	CANCEL		;CTL-X to intentionally abort?
	JP	Z,WAIT2		;yes, disconnect, jmp to menu
;
WAIT1:	POP	DE		;restore the registers
	POP	BC
	DEC	C		;count-down
	JP	NZ,WAIT
	SCF			;set carry to indicate mask not set
	RET
;.....
;
;
WAIT2:	POP	DE		;restore the registers
	POP	BC
	JP	DONETD		;disconnect
;.....
;
;	       (END OF HAYES/PMMI DIALING ROUTINE)
;=======================================================================
;			SPECIAL PMMI MENU
;
SPMEN:	LD	A,(PMMIMD)
	OR	A
	RET	Z
	CALL	J$NXTSCR
	CALL	ILPRT
	DEFB	'        Additional Subcommands for PMMI Modems'
	DEFB	CR,LF,LF
	DEFB	'  Modem control:',CR,LF
	DEFB	'    A - Answer tone for send or receive',CR,LF
	DEFB	'    O - Originate tone for send or receive',CR,LF,LF
	DEFB	'  Parity option:',CR,LF
	DEFB	'    1 - Set and check for odd parity',CR,LF
	DEFB	'    0 - Set and check for even parity',CR,LF
	DEFB	'           Both ends must be capable of these options'
	DEFB	CR,LF
	DEFB	'           which are available only in R and S modes.'
	DEFB	CR,LF
	DEFB	'           The parity checking will be part of the'
	DEFB	CR,LF
	DEFB	'           file transfer protocol.',CR,LF,LF
	DEFB	'  Speed Options:',CR,LF
	DEFB	'        After entering your primary and secondary '
	DEFB	'options,',CR,LF
	DEFB	'        you can set the modem speed by placing a '
	DEFB	' "." after',CR,LF
	DEFB	'        the options followed by the speed e.g., '
	DEFB	'300, 600.',CR,LF,LF
	DEFB	'        EXAMPLE:  SBO.600 will set the modem for '
	DEFB	'600 baud',CR,LF,0
	RET			;all done
;.....
;			(END OF PMMI MENU)
;=======================================================================
;
;
; Timer routine.  Waits 0.1 seconds for each unit in 'B' reg.
;
TIMER:	PUSH	HL
;
TIMER1:	PUSH	BC
;
TIMER2:	CALL	J$INMDM		;100 ms. delay per loop
	JP	NC,TIMER2
	POP	BC
	DEC	B
	JP	NZ,TIMER1
	POP	HL
	RET
;.....
;
;
;
;************** END OF SPECIAL HAYES/PMMI ROUTINES *********************
;
;
;=======================================================================
;
; CALCULATES DISK SPACE REMAINING IF CP/M+
;
CKCPM3:	CALL	CRLF
	LD	C,CPMVER	;check version #
	CALL	BDOS
	LD	A,L
	CP	30H		;version 3.0?
	RET	C		;use normal method if not CP/M 3.0
	POP	HL		;remove 'CALL CKCPM3' from stack
	LD	C,CURDSK
	CALL	BDOS
	LD	E,A
	LD	C,46		;CP/M 3.0 compute free space call
	CALL	BDOS
	LD	C,3		;answer is 3 bytes long (24 bits)
;
FREE30:	LD	HL,TBUF+2	;answer is located here
	LD	B,3		;convert to 'K' length
	OR	A
;
FREE31:	LD	A,(HL)
	RRA
	LD	(HL),A
	DEC	HL
	DEC	B
	JP	NZ,FREE31	;loop for 3 bytes
	DEC	C
	JP	NZ,FREE30	;shift 3 times
	LD	HL,(TBUF)	;get result in 'K'
	JP	PRTFREE		;display result
;.....
;
;
;=======================================================================
;
;
	ORG	(($+255+50)/256*256)-102	;even page for 'NUMLIB'
;
;
; PATCH AREA
;
; Patch to put copyright notice in program
;
CRITE:	CALL	J$ILPRT
	DEFB	'for Menu)',CR,LF,'Copyright (c) '
	DEFB	'1984 - Irvin M. Hoff',CR,LF,0
	RET
;.....
;
;		    end of special patch area
;-----------------------------------------------------------------------
;
; Long distance alternate dialing such as MCI, SPRINT, etc.  Must end
; with a '$', use as many commas (2 seconds delay, each) as needed to
; let the alternate dialing code return with a new dial tone.  Fill in
; any character (periods are fine) after the $ to keep number of columns
; to 24, i.e.,	'1234567,,,,12345,,$.....'   --   the first group is the
; MCI or SPRINT access number, the second group is the user number.  A
; small delay is usually required after the billing number also.
;
ALTDL1:	DEFB	'xxxxxxx,,,,,,xxxxxxxx,,$'   ;accessed by a < character
;
ALTDL2:	DEFB	'xxxxxxx,,,,,,xxxxxxxx,,$'   ;accessed by a > character
;
;=======================================================================
;
HEXSHO:	DEFB	SHOWHEX		;can easily change SHOWHEX via DDT
;
SAVSIZ:	DEFB	XFRSIZ*8	;can easily change buffer size for file
				;transfers with DDT for "NUMLIB-1" ad-
				;dress.  Normally 4k (32 records or 4k).
;
;=======================================================================
;
; Phone number library table for auto-dialing.	Each number must be as
; long as"LIBLEN" (EQU at start of program).  Some areas require extra
; characters such as:	1-313-846-7127.  Room is left for those.  Use
; a (<) for alternate dialing system #1, and a (>) for alternate dialing
; System #2.  Either would preceed the actual number, for example:
;
;	DB    'A=Alan Alda..........<123-456-7890'    ;'A'
;
; -	-     -     -	  -	-     -     -	  -	-     -     -
;
; NOTE: At least one dot (.) MUST precede the actual phone number
;
;		'----5---10---15---20---25---30--34'
NUMLIB:	DEFB	'A=Bob Robesky.......1-209-227-2083'	;'A'
	DEFB	'B=Byron McKay.......1-415-965-4097'	;'B'
	DEFB	'C=Chuck Metz........1-408-354-5934'	;'C'
	DEFB	'D=Bruce Jorgens.....1-509-255-6324'	;'D'
	DEFB	'E=Bill Earnest......1-215-398-3937'	;'E'
	DEFB	'F=Chuck Forsberg....1-503-621-3193'	;'F'
	DEFB	'G=Ron Fowler........1-414-563-9932'	;'G'
	DEFB	'H=Kirk De Haan......1-408-296-5078'	;'H'
	DEFB	'I=Jack Kinn.........1-817-547-8890'	;'I'
	DEFB	'J=Walt Jung.........1-301-661-2175'	;'J'
	DEFB	'K=Keith Petersen....1-313-759-6569'	;'K'
	DEFB	'L=Larry Snyder......1-305-671-2330'	;'L'
	DEFB	'M=Wayne Masters.....1-408-378-7474'	;'M'
	DEFB	'N=Dick Mead.........1-213-799-1632'	;'N'
	DEFB	'O=Al Mehr...........1-408-238-9621'	;'O'
	DEFB	'P=Pasadena RBBS.....1-213-577-9947'	;'P'
	DEFB	'Q=Mark Pulver.......1-312-789-0499'	;'Q'
	DEFB	'R=Bruce Ratoff......1-201-272-1874'	;'R'
	DEFB	'S=Ken Stritzel......1-201-584-9227'	;'S'
	DEFB	'T=TCBBS, Dearborn...1-313-846-6127'	;'T'
	DEFB	'U=AnaHUG RCPM.......1-714-774-7860'	;'U'
	DEFB	'V=Dave Austin.......1-707-257-6502'	;'V'
	DEFB	'W=Bill Wood.........1-619-256-3914'	;'W'
	DEFB	'X=Charlie Hoffman...1-813-831-7276'	;'X'
	DEFB	'Y=Byron Kantor......1-619-273-4354'	;'Y'
	DEFB	'Z=Spare.............1-xxx-xxx-xxxx'	;'Z'
	DEFB	'0=Paul Bagdonovich..1-201-747-7301'	;'0'
	DEFB	'1=Bill Parrott......1-913-682-3328'	;'1'
	DEFB	'2=Alex Soya.........1-305-676-3573'	;'2'
	DEFB	'3=Tony Stanley......1-912-929-8728'	;'3'
	DEFB	'4=Tampa Bay Bandit..1-813-937-3608'	;'4'
	DEFB	'5=Thousand Oaks.....1-805-492-5472'	;'5'
	DEFB	'6=Spare.............1-xxx-xxx-xxxx'	;'6'
	DEFB	'7=Spare.............1-xxx-xxx-xxxx'	;'7'
	DEFB	'8=Spare.............1-xxx-xxx-xxxx'	;'8'
	DEFB	'9=Spare.............1-xxx-xxx-xxxx'	;'9'
	DEFB	0		;end
;		'----5---10---15---20---25---30--34'
;
;-----------------------------------------------------------------------
;
; This is the storage area for the ten function keys.  The M7FNK.COM
; program dynamically allocates the storage for the keys.  Thus, no
; function key is limited to so-and-so many characters.  Rather, the
; total number of bytes in the function key library (including flags)
; is 256.
;
INTCPT:	DEFB	'^'		;intercept character (prefix)
;
FNCTBL:	DEFB	0,'DIR ',CR,0
	DEFB	1,'DIR *.* $U0AD ',CR,0
	DEFB	2,'XMODEM S ',0
	DEFB	3,'XMODEM R ',0
	DEFB	4,'BYE ',CR,0
	DEFB	5,'RBBS ',CR,0
	DEFB	6,'(vacant)',0
	DEFB	7,'(vacant)',0
	DEFB	8,'(vacant)',0
	DEFB	9,'Nice chatting, see you again soon... ',CR,0
	DEFS	256-($-FNCTBL)
;
;
;
;***********************************************************************
;
;
; P - R - O - G - R - A - M	S - T - A - R - T - S	  H - E - R - E
;
;
;***********************************************************************
;
;
START:	LD	HL,0
	ADD	HL,SP		;add the current stack pointer to 'HL'
	LD	(STACK),HL
	LD	SP,STACK	;start local stack
;
;
; The 'FIXCNT' calculations are done here and the values stored so the
; overhead of doing the calculation is not incurred in the RECV routine
; where it is desired to pick up a character from the modem data port as
; quickly as possible.
;
	LD	HL,624		;adjust to get 1 second time intervals
	CALL	FIXCNT
	LD	(TIMVAL),HL
	LD	HL,39		;should be 1/16 of above value
	CALL	FIXCNT
	LD	(QUIKTIM),HL
;
;
; Now display the program name and version number and we are under way
;
	CALL	ILPRT
	DEFB	CR,LF,'MDM',VERSION/100+'0',VERSION MOD	100/10+'0'
	DEFB	VERSION	MOD 10+'0',' modem pgm (type M ',0
	CALL	CRITE
	CALL	J$SYSVR		;give configuration message
	CALL	CRCGN		;generate tables for fast 'CRC' check
	CALL	INITAD		;initialize addresses
	CALL	INTRCPT		;establish the function key intercept
	CALL	PROCOPT		;process any options
	LD	A,(OPTION)	;any options on the command line?
	CP	' '+1
	JP	C,MENU		;if not, show the menu
;
;
; Comes here from menu once the options have been set
;
RSTRT:	LD	SP,STACK	;make sure we have a clean stack
	CALL	CKCHAR		;catch any garbage characters left over
	LD	A,(PMMIMD)
	OR	A
	JP	NZ,RSTRT1	;if yes, accept 'C' or 'D'
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,RSTRT2	;exit if neither modem-type
;
RSTRT1:	LD	A,(OPTION)	;get the option
	CP	'C'		;call (dial) function?
	JP	Z,J$DIAL	;yes, go to it
	LD	A,(PMMIMD)
	OR	A
	CALL	NZ,STBAUD	;just the PMMI has to check each time
;
RSTRT2:	CALL	MOVEFCB
	LD	A,(OPTION)	;get main option
	CP	'D'		;disconnect?
	JP	Z,DONETD	;yes, disconnect then back to the menu
	CP	'M'		;menu asked for?
	JP	Z,MENU2		;go display the menu
	CP	'R'		;want to receive a file?
	JP	Z,RCVFL		;exit if yes
	CP	'S'		;want to send a file?
	JP	Z,SNDFL		;exit if yes
	CP	'T'		;want terminal mode?
	JP	NZ,RSTRT3	;if not, exit
	XOR	A
	LD	(ECHOFLG),A	;reset echo flag
	LD	(LOCFLG),A	;reset local flag
	JP	DSKSV		;exit if yes
;
RSTRT3:	CP	'E'		;want echo mode?
	JP	NZ,NOECHO	;if not, exit
	LD	(ECHOFLG),A	;set the echo flag
	XOR	A
	LD	(LOCFLG),A	;reset local flag
	JP	DSKSV
;
NOECHO:	CP	'L'		;want local echo mode?
	JP	NZ,NOLOCL	;if not, exit
	LD	(LOCFLG),A	;set the local flag
	XOR	A
	LD	(ECHOFLG),A	;reset echo flag
	JP	DSKSV
;
NOLOCL:	CALL	NVLDMS		;say not a valid option
	JP	MENU		;then go back to the command mode
;.....
;
;
INITAD:	LD	HL,(0000H+1)	;BIOS warm reboot jump vector
	LD	DE,3
	ADD	HL,DE
	LD	(VSTAT+1),HL	;BIOS console status jump vector
	ADD	HL,DE
	LD	(VKEYIN+1),HL	;BIOS console keyboard jump vector
	ADD	HL,DE
	LD	(VTYPE+1),HL	;BIOS console CRT jump vector
	LD	DE,33
	ADD	HL,DE
	LD	(GOLIST+1),HL	;BIOS list device status jump vector
	CALL	GETUSER		;get current user number
	LD	(OLDUSER),A	;save to restore upon exit
	CALL	GTMAX		;find maximum ram for printer use
	JP	J$INITMD	;initialize non-PMMI systems if needed
;.....
;
;
; Get the function key intercept character and put in appropriate places
;
INTRCPT:LD	A,(INTCPT)	;get the function key intercept char.
	AND	07FH		;strip off any parity
	LD	(GTCMD1+1),A	;store in the menu area
	CP	' '		;printing character?
	JP	NC,INTER2	;if yes, exit
	ADD	A,40H		;change to printing character
	JP	FIXFNK		;fix-patch area of extra bytes
;
INTER1:	LD	A,'^'
	LD	(MENU3),A	;store the "control-" character
	RET
;
INTER2:	LD	(MENU3+1),A
	RET
;.....
;
;
; Process any options - put 0 in appropriate place in option table if
; option is selected
;
PROCOPT:LD	DE,FCB+1
	LD	A,(DE)
	LD	(OPTION),A
	CP	' '		;exit if no options
	RET	Z
;
OPTLP:	INC	DE
	LD	A,(DE)
	CP	' '
	JP	Z,ENDOPT
	LD	HL,OPTBL
	LD	B,OPTBE-OPTBL
;
OPTCK:	CP	(HL)
	JP	NZ,OPTNO
	CP	'O'		;want originate tones?
	LD	B,A		;store momentarily
	LD	A,ORIGMOD
	JP	Z,OPTCK1
	LD	A,B		;get the option back
	CP	'A'		;want answer tones?
	JP	NZ,OPTCK2	;if not, exit
	LD	A,ANSWMOD
;
OPTCK1:	LD	(UARTCT),A
;
OPTCK2:	LD	(HL),0
	JP	OPTLP
;
OPTNO:	INC	HL
	DEC	B
	JP	NZ,OPTCK
	CALL	NVLDMS
	POP	HL		;preserve stack
	JP	MENU
;
ENDOPT:	LD	A,(VSEEFLG)
	OR	A
	JP	NZ,CKOPT
	LD	(QFLG),A	;quiet mode for watching data items
;
CKOPT:	LD	A,(OPTION)	;check on the primary option
	CP	'D'		;going to disconnect?
	RET	Z
	CP	'E'		;return if echo option
	RET	Z
	CP	'M'		;return if help option
	RET	Z
	CP	'L'		;return if local echo option
	RET	Z
	CP	'T'		;return if terminal mode
	RET	Z
	LD	B,A		;save the primary option for a moment
	LD	A,(PMMIMD)	;PMMI modem?
	OR	A
	JP	NZ,CKOPT0	;if yes, accept 'C'
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,CKOPT1	;exit if neither
;
CKOPT0:	LD	A,B		;get the character back
	CP	'C'		;going to call a number now?
	RET	Z
;
CKOPT1:	LD	A,(NFILFLG)	;saving memory for disk file?
	OR	A
	JP	Z,CKOPT2	;if not, continue
	POP	HL		;reset the stack from 'CALL PROCOPT'
	JP	MENU0		;go show the 'FILE OPEN' message
;
CKOPT2:	LD	A,B		;get the option back
	CP	'S'
	JP	Z,CKFILE
	CP	'R'
	JP	NZ,BDOPT	;none of these, bad option
	LD	A,(BCHFLG)	;see if the batch mode flag is set
	OR	A
	RET	Z		;if yes, exit
;
CKFILE:	LD	A,(FCB+17)	;'S' and 'R' need a file name
	CP	' '
	RET	NZ		;exit if a file name is present
;
REENT:	CALL	ILPRT
	DEFB	'++ Enter primary option plus file name ++'
	DEFB	CR,LF,BELL,0
	POP	HL		;reset stack from 'CALL STFCB
	JP	MENU		;abort to command line
;.....
;
;
BDOPT:	CALL	ILPRT
	DEFB	CR,LF,'++ Bad option ++',CR,LF,LF,0
;.....
;
;
; Check for any garbage characters on line - catch and ignore
;
CKCHAR:	CALL	RCVRDY		;any characters ready to receive?
	RET	NZ		;if not, return
	CALL	I$MDDATP	;otherwise get the character and ignore
	JP	CKCHAR		;check for any additional characters
;.....
;
;
; Revised terminal routine allowing memory save.  First checks for bad
; options, to prevent wiping out the disk with accidental memory save.
;
DSKSV:	LD	A,(BCHFLG)	;batch flag set?
	OR	A
	JP	NZ,DSKSV1	;if not set, everything is normal
	LD	A,'B'		;if set, shouldn't be, so reset it
	LD	(BCHFLG),A
	JP	NOTVLD		;if set, error for 'E', 'L' or 'T'
;
DSKSV1:	LD	(XFLG),A	;will use the ASCII capture buffer size
	LD	A,(NFILFLG)	;already saving for a file?
	OR	A
	JP	Z,DSKSV2	;exit if not, and open a file
	CALL	BUFMS		;tell if buffer if on or off
	JP	TERM
;
DSKSV2:	LD	A,(FCB+1)	;first character of filename (if any)
	CP	' '		;file specified?
	JP	NZ,GOODNM	;yes, good name
	XOR	A
	LD	(NFILFLG),A	;show no file being saved
	LD	(SAVEFLG),A	;reset the flag to zero
	JP	TERM
;...
;
;
GOODNM:	CALL	ERASF
	LD	HL,FCB3
	CALL	INITFCB
	LD	HL,FCB		;move the disk name into FCB3 area
	LD	DE,FCB3
	LD	B,12
	CALL	MOVE
	LD	DE,FCB3		;now make a file from that name
	LD	C,MAKE
	CALL	BDOS
	LD	DE,FCB3		;now open the file from FCB3
	LD	C,OPEN
	CALL	BDOS
	LD	HL,BUFFER	;reset pointers to start of buffer
	LD	(HLSAV),HL
	LD	A,1
	LD	(NFILFLG),A	;show now saving to memory for disk file
	CALL	BUFMS2		;show buffer is available
;
TERM:	LD	A,(LSTTST)	;allowing the printer to be used?
	OR	A
	CALL	NZ,GOLIST	;if yes, see if anything to print
	CALL	STAT		;keyboard have a character?
	JP	Z,TERML		;if not, see if any incoming
	CALL	KEYIN		;get character from keyboard
	LD	B,A		;save for now to protect 'A' reg.
	CP	RUB		;test for rub
	JP	NZ,NOTRUB	;exit if not
	LD	A,(CONVRUB)	;convert rub to backspace?
	OR	A
	JP	Z,NOTRUB	;exit if no conversion
	LD	B,BKSP		;call it a backspace
	JP	NTOG		;go send a backspace
;
NOTRUB:	LD	A,(FNKFLG)	;get function key active flag
	OR	A
	JP	Z,NOF		;if not set yet, exit
	LD	A,B		;get character
	CP	'0'
	JP	C,NOFNK1	;ignore invalid key codes
	CP	'9'+1
	JP	NC,NOFNK1
	AND	0FH		;make 0..9
	JP	SNDFK
;
NOF:	LD	A,(INTCPT)	;check intercept character
	CP	B
	JP	NZ,NOFNK1	;skip if no function key
	LD	(FNKFLG),A	;set the function flag
	JP	TERML		;do not send the intercept character
;
NOFNK1:	XOR	A		;reset the flag
	LD	(FNKFLG),A
	LD	A,(EXACFLG)
	OR	A		;exact?
	LD	A,0		;(cannot use 'XRA A' here)
	LD	(EXACFLG),A	;clear for next time
	JP	Z,NTEXAF	;go if EXACFLG not set 'YES'
	LD	A,(LOCNXT)
	OR	A		;should we send on exacflg?
	JP	Z,NTOG		;jump if LOCONEXTCHR 'NO'
	LD	A,(EXTCHR)	;we want to send EXTCHR in any case
	CP	B
	JP	Z,NTOG		;send if EXTCHR
	JP	LOCCHK		;otherwise do local stuff
;...
;
;
NTEXAF:	LD	A,(EXTCHR)	;treat next character in special way?
	CP	B		;check against this control character
	JP	NZ,NTEXA1	;yes, set exacflg for next character
	LD	A,1
	LD	(EXACFLG),A	;set the flag
	JP	TERM		;do not send, get next character
;
NTEXA1:	LD	A,(LOCNXT)
	OR	A		;should we send if not EXACFLG?
	JP	NZ,NTOG		;jump if loconextchr 'YES'
;
LOCCHK:	CALL	XITST1		;want to exit to menu?
	LD	A,(NOCONCT)	;want to disconnect from line?
	CP	B
	JP	Z,DONETD	;if yes go disconnect
	LD	A,(TRANCHR)	;output text file to remote?
	CP	B
	JP	Z,TRNSFR
	LD	A,(TRANLOG)
	OR	A
	JP	Z,SKPLOG
	LD	A,(LOGCHR)	;send logon?
	CP	B
	JP	Z,SNDLOG
;
SKPLOG:	LD	A,(LSTTST)	;going to use the external printer?
	OR	A
	JP	Z,NOLST		;if not, skip this area
	LD	A,(LSTCHR)	;get the printer control-character
	CP	B		;did we just ask for printer control?
	JP	NZ,NOLST	;if not, exit
	LD	A,(LISTFLG)	;otherwise reset the printer toggle
	CPL
	LD	(LISTFLG),A	;and store
	CALL	CRLF
	CALL	CRLF
	CALL	LSTMS		;tell if printer is on or off now
	CALL	CRLF
	JP	TERML		;back to the terminal mode again
;.....
;
;
NOLST:	LD	A,(BRKCHR)	;PMMI break?
	CP	B
	JP	Z,BREAK
	LD	A,(PMMIMD)	;using a PMMI board?
	OR	A
	JP	Z,NOLST1	;if not, skip the next few lines
	LD	A,(CHGBAUD)	;PMMI change baud?
	CP	B
	PUSH	AF
	PUSH	HL
	CALL	Z,J$NWBAU
	POP	HL
	POP	AF
	JP	Z,TERML
;...
;
;
NOLST1:	LD	A,(UNSAVCH)	;close input buffer?
	CP	B
	JP	Z,NOLST2	;if yes, disable copy
	LD	A,(SAVECHR)	;open input buffer?
	CP	B
	JP	NZ,NTOG
	LD	A,(NFILFLG)	;do not allow save if flag is set
	OR	A
	JP	Z,TERML
	JP	NOLST3
;
NOLST2:	XOR	A		;stop copy into file
;
NOLST3:	LD	(SAVEFLG),A
	CALL	BUFMS
	JP	TERM		;get next character
;.....
;
;
;***********************************************************************
;
;			 SND A CP/M FILE
;
;***********************************************************************
;
;
SNDFL:	XOR	A		;set to checksum initially on send
	LD	(CRCFLAG),A	;  initially on send
	CALL	CKCHAR		;catch any garbage characters
;
SNDFL1:	LD	A,(BCHFLG)	;check if multiple file
	OR	A		;  mode is set.
	JP	NZ,SNDC1
	CALL	ILPRT
	DEFB	'Ready to send in batch mode',CR,LF,0
;
SNDFL2:	CALL	J$PRITY
	LD	A,YES		;indicate send for batch mode
	LD	(SNDFLG),A
	LD	A,(FSTFLG)	;if first time through
	OR	A		;  scan the command line
	CALL	Z,TNMBUF	;  for multiple names
	CALL	SNDFN		;sends file name to receive
	PUSH	AF
	CALL	CRLF
	CALL	SHOFIL
	LD	A,' '
	CALL	TYPE
	POP	AF
	JP	NC,SNDC2	;carry set means no more files
	LD	A,'B'		;stop batch
	LD	(BCHFLG),A	;mode option
	LD	A,EOT		;final transfer end
	CALL	SND
	JP	DONE
;
SNDC1:	LD	A,(FCB+1)
	CP	' '
	JP	Z,BLKFILE
;
SNDC2:	CALL	CNREC		;get number of records
	CALL	OPENFIL
	LD	E,120		;wait 2 minutes maximum
	CALL	WAITNAK
;
SNDLP:	CALL	CKABORT		;want to terminate while sending file?
	CALL	RDRECD
	JP	C,SNDEOF
	CALL	INCRRNO
	LD	A,1
	LD	(ERRCT),A
;
SNDRPT:	CALL	CKABORT		;want to terminate while sending file?
	CALL	SNDHDR
	CALL	SNDREC
	LD	A,(CRCFLAG)
	OR	A
	CALL	Z,SNDCKS
	CALL	NZ,SNDCRC
	CALL	GTACK
	JP	C,SNDRPT
	JP	SNDLP
;.....
;
;
SNDEOF:	LD	A,EOT
	CALL	SND
	CALL	GTACK
	JP	C,SNDEOF
	JP	DONE
;.....
;
;
;***********************************************************************
;
;		       RECEIVE A CP/M FILE
;
;***********************************************************************
;
;
RCVFL:	LD	A,(CRCDFLT)	;get mode requested by operator
	LD	(CRCFLAG),A	;store it
;
RCVFL1:	CALL	J$PRITY
	LD	A,(BCHFLG)	;check if multiple file mode
	OR	A
	JP	NZ,RCVC1	;if not, exit
	LD	A,NO		;flag where to return
	LD	(SNDFLG),A	;  for next file transfer
	CALL	GETFN		;get the file name
	JP	NC,RCVC2	;carry set means no more files
	LD	A,'B'		;stop batch
	LD	(BCHFLG),A	;mode option
	JP	DONE
;
RCVC1:	LD	A,(FCB+1)	;make sure file is named
	CP	' '
	JP	Z,BLKFILE
	JP	RCVC3
;
RCVC2:	CALL	SHOFIL		;show the file name
	LD	A,' '
	CALL	TYPE
	CALL	SNDPRG		;get progress and wait for quiet line
	CALL	CKCPM2
	CALL	CRLF
	CALL	CKBAKUP
;
RCVC3:	CALL	ERASF
	CALL	MAKEFIL
	CALL	WAITQ1
	LD	A,(BCHFLG)	;do not print message if in batch mode
	OR	A
	JP	Z,RCVFST
	CALL	ILPRTQ
	DEFB	'File open, ready to receive',CR,LF,0
;
RCVFST:	LD	A,(CRCFLAG)
	OR	A
	JP	Z,RCVNKM	;if in 'CRC' mode
	CALL	ILPRTQ		;then say so
	DEFB	'CRC in effect',CR,LF,0
	LD	A,CRC
	JP	RCVLP0
;
RCVNKM:	CALL	ILPRTQ		;else say 'CHECKSUM' mode
	DEFB	'Checksum in effect',CR,LF,0
	LD	A,NAK
;
RCVLP0:	PUSH	AF
	CALL	ILPRT
	DEFB	'Waiting.....',0
;
NOPRG:	POP	AF
	CALL	SND
;
RCVLP:	CALL	RCVRECD
	JP	C,RCVEOT
	CALL	REPORT		;show record received if not in quiet
	CALL	WRRECD
	CALL	INCRRNO
	CALL	SNDACK
	JP	RCVLP
;
RCVEOT:	CALL	WRBLOCK
	CALL	SNDACK
	CALL	CLOSFIL
	JP	DONE
;
SNDACK:	LD	A,ACK
	CALL	SND
	RET
;
;.....
;
;
;=================== FILE TRNSFR IN T-MODE ===========================
;
;
; File transfer routine - called with CTL-T from terminal mode.  Trans-
; fer may be cancelled while sending, by using CTL-X.
;
TRNSFR:	LD	HL,FCB4
	CALL	INITFCB		;initializes FCBs pointed
	LD	HL,FCB+16	;  to by 'HL' register
	CALL	INITFCB
;
;
; Get name of file to send in "T" (terminal) mode
;
GET:	CALL	ILPRT
	DEFB	CR,LF,'File name to send? (CR to abort): ',0
	LD	DE,CMDBUF
	CALL	INBUF
	LD	A,(CMDBUF+2)	;was file entered?
	CP	' '
	JP	Z,RETRN		;if not probably wanted to quit
	LD	DE,CMDBUF
	LD	HL,FCB4
	CALL	CMDLINE
	LD	DE,FCB4
	LD	C,OPEN
	CALL	BDOS
	CP	0FFH		;return with 0FFH means 'NO SUCH FILE'
	JP	Z,TRANSL
	LD	A,(XONWAIT)	;waiting for X-on to send next line?
	OR	A
	JP	NZ,DLYSAV	;if yes, skip additional delays
;
;
; Choice of normal speed or delays between characters / lines
;
	CALL	ILPRT
	DEFB	'Want to include time delays? (Y/N): ',0
	CALL	KBDCHR
	CP	'N'		;if 'N' send normal speed
	JP	Z,DLYSAV
	XOR	A		;otherwise use character/line delays
;
DLYSAV:	LD	(DLYFLG),A	;store the decision
	CALL	CRLF
	LD	DE,CMDBUF+2	;make sure cmdbuf has been selected
	LD	C,STDMA
	CALL	BDOS
;
;
; Get 128-byte record
;
READM:	LD	DE,FCB4
	LD	C,READ
	CALL	BDOS
	OR	A		;check for a good read
	JP	Z,READM1
	DEC	A		;check for end of file to send
	JP	Z,RETRNS
	CALL	ERXIT		;neither of those, was a read error
	DEFB	'++ DISK READ ERROR ++','$'
;
;
; Successful read, so send the record
;
READM1:	CALL	SND80C		;send one 128-char record
	CP	EOFCHAR		;end of file - omit if object
	JP	Z,RETRNS	;  code is to be sent.
	CP	CANCEL		;cancellation?
	JP	NZ,READM
;
RETRN:	CALL	ILPRT
	DEFB	CR,LF,LF,'(in Terminal-mode now)',CR,LF,LF,0
	CALL	SNDNOW		;insures last character is finished
	CALL	CKCHAR		;catch any echo character on line
	JP	TERM		;finished, back to t-mode
;.....
;
;
RETRNS:	CALL	ILPRT
	DEFB	CR,LF,'[Transfer completed]',0
	JP	RETRN
;.....
;
;
TRANSL:	CALL	ILPRT
	DEFB	CR,LF,BELL,'++ FILE NAME ERROR ++ ',CR,LF,0
	JP	GET
;.....
;
;
; Send one 128-byes record
;
SND80C:	LD	B,128		;will send a maximum of 128 character
	LD	HL,CMDBUF+2	;they are in the cmdbuf area
;
SNDCH1:	PUSH	DE
	CALL	SPEED		;0-90 ms. delay between characters
	POP	DE
	LD	A,(HL)
	CP	EOFCHAR
	RET	Z
	CALL	MDOUT		;send the character to modem
	CALL	STAT		;test to see if
	OR	A		;cancellation requested
	JP	Z,SKIP1
	CALL	KEYIN
	CP	CANCEL
	RET	Z
;
SKIP1:	INC	HL
	DEC	B
	JP	NZ,SNDCH1
	RET
;.....
;
;
; Send the character to the output
;
MDOUT:	PUSH	AF		;save the character so can use 'A' reg.
	CP	LF
	JP	NZ,MDOUTL
	LD	A,(ADDLFD)	;going to send the line feed to modem?
	OR	A
	JP	NZ,MDOUTL	;if yes, exit
	POP	AF		;get the char. back (a line feed)
	CALL	TYPE		;show on CRT, do not send to modem
	RET
;
MDOUTL:	LD	A,(XOFFTST)	;waiting for X-off, X-on ?
	OR	A
	CALL	NZ,TXOFF	;if yes, go check
	CALL	SNDRDY		;wait until modem is ready to send
	JP	NZ,MDOUTL
	POP	AF		;get the character back
	CALL	TYPE		;send character to CRT
	CALL	O$MDDATP	;send character to modem
	CP	CR		;was it an end of line?
	RET	NZ		;if yes, see if any delay is needed
;
;
; Delay to allow slow BBS systems (most use BASIC) to enter the line.
; Choice of 0-9 for about 100 ms. each, maximum of 900 ms.
;
MDOUTN:	LD	A,(XONWAIT)	;wait for X-on after CR?
	OR	A
	JP	NZ,WATXON	;if yes, handle separately
	LD	D,10
;
MDOUTT:	PUSH	DE
	CALL	SPEED1		;10 ms delay
	POP	DE
	DEC	D
	JP	NZ,MDOUTT	;10 loops for 100 ms.
	RET
;.....
;
;
; Add from 0 to 90 ms. delay between characters for slow (most use
; BASIC) bulletin board systems.  Also used to add 0-900 ms. delay
; between lines.
;
SPEED:	LD	A,(BYTDLY)	;get delay between characters (0-9)
	JP	SPEED1+3	;1=10 ms, 5=50 ms, 9=90 ms, etc.
;
SPEED1:	LD	A,(CRDLY)	;get delay after crlf (0-9)
	OR	A		;100 ms, 5=500 ms, 9=900 ms, etc.
	RET	Z		;if no delay needed, return
	LD	C,A		;store number requested in c-reg.
	LD	A,(DLYFLG)	;want any delays this file?
	OR	A
	RET	NZ		;if not, skip this section
;
SPEED2:	CALL	SPEED3		;outer loop
	DEC	C
	JP	NZ,SPEED2
	RET			;done whenever the c-reg. is zero
;...
;
;
SPEED3:	PUSH	HL		;save current 'HL' value
	LD	HL,20
	LD	A,(XOFFTST)
	OR	A
	JP	Z,SPEED4
	LD	HL,20		;adjust for 'X-OFF' testing
	LD	A,(ECHOFLG)
	OR	A
	JP	Z,SPEED4
	LD	A,(LOCFLG)
	OR	A
	JP	Z,SPEED4
	LD	HL,25		;adjust for remote echo
;
SPEED4:	CALL	FIXCNT		;multiply delay by clock speed
	EX	DE,HL		;transfer delay to 'DE'
	POP	HL		;restore current 'HL' from"speed3"
;
SPEED5:	DEC	DE		;inner loop
	LD	A,(XOFFTST)
	OR	A
	CALL	NZ,TXOFF
	LD	A,E
	OR	D
	JP	NZ,SPEED5
	RET
;...
;
;
TXOFF:	CALL	RCVRDY
	RET	NZ
	CALL	I$MDDATP
	AND	7FH
	CP	XOFF
	CALL	Z,WATXON
	RET
;.....
;
;
WATXON:	CALL	RCVRDY		;have a character? (like x-on)
	JP	NZ,WATXN1	;if no character see if want to abort
	CALL	I$MDDATP
	AND	7FH		;strip off any parity
	CP	XON		;see if character was X-on
	RET	Z		;if yes, keep going
;
WATXN1:	CALL	STAT		;test to see if requesting cancellation
	JP	Z,WATXON
	CALL	KEYIN		;can abort if the x-on never comes
	CP	CANCEL		;CTL-X to abort?
	JP	NZ,WATXON	;if not, keep going
	RET	Z
;.....
;
;================ END OF FILE TRNSFR IN T-MODE =======================
;
;
;***********************************************************************
;
;			   SUBROUTINES
;
;***********************************************************************
;
; Returns with the zero flag set if retry requested.  If using multi-
; file (batch) modem, then no questions asked, just quit.
;
CKQIT:	LD	A,(BCHFLG)	;using batch mode now?
	OR	A
	JP	Z,ABORT		;quit if using batch mode
;
CKQIT1:	LD	A,1
	LD	(ERRCT),A
	CALL	ILPRT
	DEFB	CR,LF,'Multiple errors encountered.',CR,LF
	DEFB	'Type Q to quit, R to retry:  ',BELL,0
	CALL	KEYIN
	PUSH	AF
	CALL	CRLF
	POP	AF
	CALL	UCASE		;instead of 'ANI 5FH'
	CP	'R'
	JP	Z,RCVRECD	;if 'R' keep trying
	CP	'Q'
	JP	NZ,CKQIT1
	JP	ABORT
;.....
;
;
; Show the file name as stored in the FCB but in CP/M format
;
SHOFIL:	LD	A,(QFLG)	;can type it if no 'QFLG'
	OR	A
	RET	Z
	LD	HL,FCB+1
;
SHONM:	XOR	A
	LD	(FTYCNT),A
	LD	C,11
;
PRNAM:	CALL	FTYTST
	INC	HL
	DEC	C
	JP	NZ,PRNAM
	RET
;.....
;
;
; Give report of received records as they occur
;
REPORT:	LD	A,(QFLG)
	OR	A
	RET	Z
	LD	HL,(RECNO)	;get record number
	INC	HL
	CALL	ILPRT
	DEFB	CR,'Received # ',0
	CALL	DECOUT		;print record number in decimal
	CALL	ILPRT
	DEFB	' ',0
;
	LD	A,(HEXSHO)
	OR	A
	RET	Z
	CALL	ILPRT
	DEFB	'(', 0
	CALL	DHXOUT		;16 bit hex conversion and output
	CALL	ILPRT
	DEFB	'H) ',0
	RET
;.....
;
;
FTYTST:	LD	A,(FTYCNT)
	INC	A
	LD	(FTYCNT),A
	CP	9		;are we at the file type?
	JP	Z,SPCTST	;go if so
;
ENDSPT:	LD	A,(HL)
	CP	' '		;test for space
	CALL	NZ,TYPE		;type if not
	RET
;.....
;
;
SPCTST:	LD	A,(HL)
	CP	' '		;test for space in 1st file type byte
	RET	Z		;do not output period if space
	LD	A,'.'
	CALL	TYPE
	JP	ENDSPT		;output 1st file type byte
;.....
;
;
; Get sender's progress report if it is present and wait for line to get
; quiet
;
SNDPRG:	LD	B,5		;wait up to 5 seconds
	CALL	RECV
	CALL	TYPE		;show the progress report from sender
	JP	NC,SNDPRG
	RET
;.....
;
;
SNDFN:	CALL	ILPRTQ
	DEFB	'Awaiting name NAK  ',CR,LF,0
	CALL	GTACK
	CALL	C,SNDACK
	LD	HL,FILECT
	DEC	(HL)
	JP	M,NOMRN
	LD	HL,(NBSAVE)	;get file name in FCB
	LD	DE,FCB
	LD	B,12
	CALL	MOVE
	LD	(NBSAVE),HL
	CALL	SNDNM		;send it
	OR	A		;clear carry
	RET
;.....
;
;
NOMRN:	LD	A,EOT
	CALL	SND
	SCF
	RET
;.....
;
;
; Wait for line to get quiet and gobble characters
;
WAITQ1:	LD	B,1
	CALL	RECV
	JP	NC,WAITQ1
	RET
;.....
;
;
SNDNM:	PUSH	HL
;
SNDNM1:	LD	D,11		;count characters in name
	LD	C,0		;initialize checksum
	LD	HL,FCB+1	;address name
;
NAMLPS:	LD	A,(HL)		;send name
	AND	7FH		;strip high order bit so CP/M 2.x
	CALL	SND		;  will not send R/O file designation
;
ACKLP:	PUSH	BC		;save checksum
	LD	B,5		;wait for receiver to acknowledge
	CALL	RECV		;  getting the letter
	POP	BC
	JP	C,SCKSER
	CP	ACK
	JP	NZ,ACKLP
	INC	HL		;next character
	DEC	D
	JP	NZ,NAMLPS
	LD	A,EOFCHAR	;tell receiver the end of name
	CALL	SND
	LD	D,C		;save checksum
;
CKSMLP:	LD	B,5		;wait up to 5 seconds
	CALL	RECV		;get checksum
	CP	D
	JP	NZ,SCKSER	;exit if bad name
	LD	A,OKNMCH	;good name-tell receiver
	CALL	SND
	POP	HL
	RET
;.....
;
;
SCKSER:	LD	A,BDNMCH	;bad name-tell receiver
	CALL	SND
	CALL	ILPRT
	DEFB	CR,LF,'++ ERROR sending name  ++',CR,LF,0
	LD	E,120		;do handshaking over (2 minutes maximum)
	CALL	WAITNLP		;don't print "WAITING READY SIGNAL" msg.
	CALL	SNDACK
	JP	SNDNM1
;.....
;
;
; This patch fixes a trivial problem with the display of the function
; key group on the menu.  It uses some of the extra bytes availble in
; this area from the CKSMLP fix.
;
FIXFNK:	LD	(MENU3+1),A	;store the character in the menu display
	CP	'['		;'ESC' character, printed
	JP	NC,INTER2	;if 'ESC' or more, exit
	JP	INTER1		;otherwise include a '^'
;.....
;
;
;
; Patch to close FCB3 instead of FCB when in disk-capture mode.
;
WRERRSP:CALL	WRFIL2		;close FCB3 file
	JP	WRERR1		;go write 'DISK FULL' message and quit
;.....
;
;
EXTRA:	DEFB	'123456789 '	;10 extras from CKSMLP (there were 27)
;
;
GETFN:	LD	HL,FCB
	CALL	INITFCB+2	;does not initialize drive
	CALL	ILPRTQ
	DEFB	'Awaiting file name',CR,LF,0
	CALL	HSNAK
	CALL	GETNM		;get the name
	CP	EOT		;if EOT, then no more files
	JP	Z,NOMRNG
	OR	A		;clear carry
	RET
;
NOMRNG:	SCF
	RET
;...
;
;
GETNM:	PUSH	HL
;
GETNM1:	LD	A,0FFH
	LD	(FLTRFLG),A
	LD	C,0		;initialize checksum
	LD	HL,FCB+1
;
NAMELPG:LD	B,5
	CALL	RECV		;get the character
	PUSH	BC
	PUSH	AF
	LD	A,0FFH
	LD	(TIMFLG),A
	LD	B,1
	CALL	RECV
	XOR	A
	LD	(TIMFLG),A
	POP	AF
	POP	BC
	JP	NC,GETNM3
	CALL	ILPRTQ
	DEFB	'Time out receiving filename',CR,LF,0
	JP	GCKSER
;
GETNM3:	CP	EOT		;if EOT, then no more files
	JP	Z,GNRET
	CP	EOFCHAR		;got end of name
	JP	Z,ENDNAM
	PUSH	AF
	PUSH	BC
	CALL	SNDACK
	POP	BC
	POP	AF
	LD	(HL),A		;put name in FCB
	INC	HL		;get next character
	LD	A,L		;do not let noise cause overflow
	CP	7FH		;  into the program area
	JP	Z,GCKSER
	JP	NAMELPG
;
ENDNAM:	XOR	A
	LD	(FLTRFLG),A
	LD	A,C		;send checksum
	LD	D,C
	CALL	SND
;
NMLP1:	LD	B,5		;wait up to 5 second to see if
	CALL	RECV		;  the checksum is good
	CP	OKNMCH		;yes if 'OKNMCH' sent
	JP	Z,GNRET
	CP	D
	JP	Z,NMLP1		;in case it is echo of send
	CP	CR
	JP	Z,NMLP1
	CP	LF
	JP	Z,NMLP1
;
GCKSER:	LD	HL,FCB		;clear FCB (except drive) since it
	CALL	INITFCB+2	;  might be damaged
	CALL	ILPRTQ
	DEFB	CR,LF,'**  Checksum error  **',CR,LF,0
	XOR	A
	LD	(FLTRFLG),A
	CALL	HSNAK		;do handshaking over
	JP	GETNM1
;
GNRET:	PUSH	AF
	XOR	A
	LD	(FLTRFLG),A
	POP	AF
	POP	HL
	RET
;
HSNAK:	LD	E,180		;3 minute wait for file name
	XOR	A
	LD	(FLTRFLG),A
;
HSNAK1:	CALL	CKABORT		;want to abort?
	LD	A,NAK		;send 'NAK' until receiving 'ACK'
	CALL	SND
	LD	B,1		;wait up to 1 second for a character
	CALL	RECV
	CP	ACK		;'ACK' is what we were waiting for
	RET	Z
	DEC	E
	JP	NZ,HSNAK1
	JP	ABORT		;back to command line
;.....
;
;
TNMBUF:	LD	A,1		;call from 'SNDFL' only once
	LD	(FSTFLG),A
	XOR	A
	LD	(FILECT),A
	CALL	SCAN
	LD	HL,NAMEBUF
	LD	(NBSAVE),HL	;save address of 1st name
;
TNLP1:	CALL	TRTOBUF
	LD	HL,FCB
	LD	DE,FCBBUF
	CALL	CMDLINE		;parse name to CP/M format
;
TNLP2:	CALL	MFNAM		;search for names (wildcard format)
	JP	C,NEXTNM
	LD	A,(FCB+10)	;if CP/M 2.x $SYSs file
	AND	80H		;  do not send
	JP	NZ,TNLP2
	LD	HL,(NBSAVE)	;get name
	LD	DE,FCB		;move it to FCB
	EX	DE,HL
	LD	B,12
	CALL	MOVE
	EX	DE,HL
	LD	(NBSAVE),HL	;address of next name
	LD	HL,FILECT	;count files found
	INC	(HL)
	JP	TNLP2
;.....
;
;
NEXTNM:	LD	HL,NAMECT	;count names found
	DEC	(HL)
	JP	NZ,TNLP1
	LD	HL,NAMEBUF	;save start of buffer
	LD	(NBSAVE),HL
	LD	A,(FILECT)
	CP	64+1		;no more than 64 transfers
	RET	C
	LD	A,64		;only transfer first 64
	LD	(FILECT),A
	RET
;.....
;
;
; Tells when buffer is opened/closed for memory save to write on disk
;
BUFMS:	CALL	ILPRT
	DEFB	CR,LF,'** Memory buffer ',0
	LD	A,(SAVEFLG)
	OR	A
	JP	Z,BUFMS1
	CALL	ILPRT
	DEFB	'open **',CR,LF,LF,';',0
	RET
;...
;
;
BUFMS1:	CALL	ILPRT
	DEFB	'closed **',CR,LF,LF,0
	RET
;...
;
;
BUFMS2:	CALL	ILPRT
	DEFB	CR,LF,'** Memory buffer available **',CR,LF,0
	RET
;.....
;
;
; Clear the screen and return to the menu command
;
XITMNU:	CALL	CRLF
	CALL	CLREOS		;clear line to clean up any mess
	JP	MENU0
;.....
;
;
; Checks to see if the modem has a character ready
;
RCVRDY:	CALL	I$MDCTL1
	CALL	A$MDRCVB
	JP	C$MDRCVR
;.....
;
;
; Checks to see if the modem is ready to receive a character
;
SNDRDY:	CALL	I$MDCTL1
	CALL	A$MDSNDB
	JP	C$MDSNDR
;.....
;
;
SNDNOW:	CALL	XITST		;see if want to quit now
	CALL	SNDRDY		;ready to send a character?
	JP	NZ,SNDNOW	;if not ready wait some more
	RET			;exit if ready
;.....
;
;
; Send the log-on message when requested
;
SNDLOG:	LD	HL,(LOGONPTR)	;'HL' points to start of logon message
	CALL	LOGLP
	JP	TERML
;...
;
;
LOGLP:	CALL	SNDNOW		;wait until modem is ready
	LD	A,(HL)		;get logon byte
	OR	A		;last character in string is '0'
	RET	Z		;return if finished
	CALL	O$MDDATP	;otherwise send the character
	CALL	LOGLP1		;check for echo character and display it
	INC	HL		;next location in message
	JP	LOGLP		;get next character
;.....
;
;
LOGLP1:	CALL	J$INMDM		;get the echo character
	CALL	C,J$INMDM	;if none, try once more
	RET	C		;if no character do not try to print
	AND	7FH		;strip off any parity
	JP	TYPE		;display the character, then return
;.....
;
;
; Check for exit character
;
XITST:	CALL	STAT		;anything on keyboard?
	RET	Z
	CALL	KEYIN		;get it, then
	LD	B,A		;save to protect the 'A' register
;
XITST1:	LD	A,EXITCHR	;exit character
	CP	B		;asking to exit to menu?
	RET	NZ		;if not, back to work
	POP	HL		;clear the stack from 'CALL'
	JP	XITMNU		;exit to the menu
;.....
;
;
LSTMS:	CALL	ILPRT
	DEFB	'Printer buffer is ',0
	LD	A,(LISTFLG)	;see if printer should be on or off
	OR	A
	JP	Z,LSTMS1
	CALL	ILPRT
	DEFB	'ON',CR,LF,0
	RET
;...
;
;
LSTMS1:	CALL	ILPRT
	DEFB	'OFF',CR,LF,0
	RET
;.....
;
;
; Special function key handler.  This routine is entered with the
; function key number (0..9) in A.  The corresponding function key is
; then transmitted.
;
SNDFK:	PUSH	HL		;save register
	LD	HL,FNCTBL	;point to function key codes
;
SFK1:	CP	(HL)		;this the one?
	INC	HL		;point to next byte
	JP	NZ,SFK1		;loop until found
	CALL	LOGLP		;send the char
	POP	HL
	XOR	A		;reset the function flag
	LD	(FNKFLG),A
	JP	TERML
;.....
;
;
; Send keyboard character to modem, also to console if "E" or "L".  If
; "E" include a LF after a CR, if either, include a LF if toggle is set.
;
NTOG:	CALL	SNDCHR		;send char. in 'B' to modem
	LD	A,(LOCFLG)	;using the local mode?
	OR	A
	JP	NZ,LTYPE	;if yes, show the character
	LD	A,(ECHOFLG)	;in echo mode?
	OR	A
	JP	Z,TERML		;if not, see if it was a 'CR'
;
LTYPE:	LD	A,B		;get the character back
	CALL	TYPE		;show on the local CRT
	CALL	CKSAV		;to store local if buffer open
	CALL	CHKPRNT		;put on printer if running
;
CHKCR:	LD	A,CR
	CP	B
	JP	NZ,TERML	;if not CR, all done
	LD	A,(ECHOFLG)	;in echo mode now?
	OR	A
	JP	NZ,CHKLF	;if yes add a line feed
	LD	A,(ADDLFD)	;going to add a line feed in 'L' mode?
	OR	A
	JP	Z,TERM		;if not, exit
;
CHKLF:	LD	B,LF
	JP	NTOG		;send locally and to remote
;.....
;
;
TERML:	CALL	RCVRDY		;character on the receive-ready line?
	JP	NZ,TERM		;if not, exit
	CALL	I$MDDATP	;get the character
	AND	7FH		;strip parity
	JP	Z,TERM		;do not bother with nulls
	CP	RUB
	JP	Z,TERM		;do not bother with rubouts for fill
	LD	B,A		;store temporarily
	LD	A,(IGNRCTL)	;ignoring all but necessary CTL-chars?
	OR	A
	JP	Z,GIVLF		;if zero, display them all
	LD	A,B
	CP	' '
	JP	NC,GIVLF	;display all printing characters
	CP	'G'-40H		;^G for bell
	JP	C,TERM		;ignore CTL-characters less than ^G
	CP	CR+1
	JP	NC,TERM		;ignore CTL-charsacters more than ^M
;
GIVLF:	LD	A,B		;get the character back
	CALL	TYPE		;show it on the CRT
	CALL	CKSAV		;saving for disk file?
	CALL	CHKPRNT		;printer running?
	LD	A,(ECHOFLG)	;going to echo the character?
	OR	A
	JP	Z,NOECH		;if not, do not resend
;
GIVLF1:	CALL	SNDCHR		;send character in 'B' to modem
;
NOECH:	LD	A,CR
	CP	B		;was it a 'CR' just now?
	JP	NZ,TERM		;if not, all done so exit
	LD	A,(ECHOFLG)	;in the echo mode?
	OR	A
	JP	Z,TERM
	CALL	SNDNOW		;modem ready for a character?
	LD	B,LF
	JP	GIVLF		;send LF
;.....
;
;
; See if putting character into memory for a disk file
;
CKSAV:	LD	A,(SAVEFLG)	;saving to disk?
	OR	A
	RET	Z		;if not, exit
	LD	HL,(HLSAV)	;get last address
	LD	(HL),B		;store this character
	INC	HL		;increment for next character
	LD	(HLSAV),HL	;remember that location
	LD	A,LF
	CP	B		;this character a line feed?
	JP	NZ,CKSAV1	;  type ";" after each line feed
	LD	A,CR		;insure at left column with a LF
	CALL	TYPE
	CALL	TYPSEM		;show a ';' on CRT
;
CKSAV1:	LD	A,H
	LD	HL,BUFTOP	;get the address at top of buffer
	CP	H
	CALL	Z,DCTLS		;if different, buffer is not full
	RET
;.....
;
;
; Memory buffer is full, send a X-OFF (CTL-S, DC3), save any extra in-
; coming characters, then dump to disk, reset buffer to include those
; characters.
;
DCTLS:	CALL	SNDNOW		;modem ready for a character?
	LD	A,XOFF		;send a CTL-S to stop remote computer
	CALL	O$MDDATP
	CALL	CHKPRNT		;insure character gets to the printer
	LD	HL,BUFFDSK	;address of auxiliary buffer
	CALL	GTDSK		;put any extra chars. into aux. buffer
	PUSH	DE		;save the number of aux. chars.
	LD	A,1		;show we put something in the buffer
	LD	(WRFLG),A	;  to protect erasing an empty file
	LD	HL,(HLSAV)	;find current end of buffer
	CALL	WRDSK1		;write the records
	POP	DE		;get auxiliary character count back
	LD	HL,BUFFER	;start again at bottom of buffer
	XOR	A		;set 'A' to zero
	CP	D		;see if any count in 'D'
	JP	Z,DCTLQ		;if nothing, exit and continue
	LD	BC,BUFFDSK	;address of auxiliary buffer
;
;
; Move the characters from the auxiliary buffer to the main buffer and
; display
;
DCTLS1:	LD	A,(BC)		;get the character there
	LD	(HL),A		;store in main buffer
	CALL	TYPE		;show on CRT
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	B,A
	CALL	CHKPRNT
	POP	AF		;get the character again
	POP	BC
	POP	DE
	POP	HL
	CP	LF
	CALL	Z,TYPSEM
	INC	HL		;next buffer position
	INC	BC		;next auxiliary buffer position
	DEC	D		;one less to go
	JP	NZ,DCTLS1	;if not zero, keep going
	LD	B,0		;falls through to 'CHKPRNT' next
;
DCTLQ:	LD	(HLSAV),HL	;next position to store in buffer
	CALL	SNDNOW
	LD	A,XON		;allow remote input to continue
	JP	O$MDDATP
;.....
;
;
; Gets any incoming characters after sending an XOFF and stores at HL.
; Returns with number of characters stored in D-reg.
;
GTDSK:	LD	D,0		;character count in buffer
	LD	E,128		;maximum for buffer length
;
GTDSK1:	CALL	J$INMDM		;get any character
	RET	C		;if none, finished
	CP	' '
	JP	NC,GTDSK2	;if greater, handle normally
	CP	CR+1		;ignore CTL-characters > CR
	JP	NC,GTDSK1
;
GTDSK2:	LD	(HL),A		;store
	INC	HL		;next buffer location to use
	INC	D		;increment character count
	DEC	E		;room for one less
	JP	NZ,GTDSK1	;if room in buffer, keep going
	RET			;if buffer is filled, exit
;.....
;
;
; See if printing the character, if yes, put into buffer
;
CHKPRNT:LD	A,(LISTFLG)	;printer in use?
	OR	A
	RET	Z		;return if not
	LD	HL,(HLSAV1)	;get input address
	LD	(HL),B		;save this character there
	INC	HL		;increment the buffer location
	LD	(HLSAV1),HL	;store for next character
	LD	A,(MAXRAM)	;see if at top of buffer yet
	CP	H
	CALL	Z,PCTLS		;if different, buffer is not full
	RET
;.....
;
;
; Memory buffer is full, send a X-OFF (CTL-S, DC3), save any extra in-
; coming characters, then dump to disk, reset buffer to include those
; characters.
;
PCTLS:	CALL	SNDNOW		;wait until modem is ready
	LD	A,XOFF		;send a CTL-S to stop remote computer
	CALL	O$MDDATP
	LD	HL,BUFFPNT	;address of auxiliary buffer
	CALL	GTDSK		;put any extra chars. into aux. buffer
	LD	A,D		;get the aux. buffer character count
	LD	(DSTORE),A	;save for later
	RET
;.....
;
;
; Output has now caught up to the input and both are at top of buffer
;
PCTLS1:	LD	A,(DSTORE)	;get the aux. buffer character count
	LD	D,A		;put into 'D' register
	XOR	A		;set 'A' to zero
	CP	D		;see if any count in 'D'
	LD	HL,PBUFF	;address at start of printer buffer
	JP	Z,PCTLQ		;if nothing, exit and continue
	LD	BC,BUFFPNT	;address of auxiliary buffer
;
;
; Move the characters from the aux. buffer to the main buffer and display
;
PCTLS2:	LD	A,(BC)		;get the character there
	LD	(HL),A		;store in main buffer
	CALL	TYPE		;show on CRT
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	B,A
	CALL	CKSAV
	POP	AF
	POP	BC
	POP	DE
	POP	HL
	CP	LF
	CALL	Z,TYPSEM
	INC	HL		;next buffer position
	INC	BC		;next auxiliary buffer position
	DEC	D		;one less to go
	JP	NZ,PCTLS2	;if not zero, keep going
;
PCTLQ:	LD	(HLSAV1),HL	;next position to store in buffer
	LD	HL,PBUFF	;start of buffer location
	LD	(HLSAV2),HL	;output to start of buffer
	CALL	SNDNOW		;wait until modem is ready
	LD	A,XON		;send start character
	JP	O$MDDATP	;  to remote computer, back to work
;.....
;
;
; List the character on the printer if it is ready, then see if at the
; top of the buffer.
;
GOLIST:	CALL	$-$		;get the printer status - filled in
	OR	A		;by 'INITAD' routine
	RET	Z		;return if printer not ready
;
;
; Compare input and output pointers.  If at same address, nothing to
; print.
;
	CALL	CMP$I$O		;if the same, nothing to print
	RET	Z
;
;
; If not the same, print the character
;
GOLIST1:PUSH	HL		;save current buffer address
	LD	E,(HL)		;get the character to display
	LD	C,LIST		;list rutine
	CALL	BDOS
	POP	HL		;restore current buffer address
	INC	HL		;increment pointer for next position
	LD	(HLSAV2),HL	;store for next time through here
;
;
; See if the output is at the end of the buffer now.  If yes, go put
; the auxiliary characters into the start of the buffer.
;
	LD	A,(MAXRAM)	;check for end of buffer area
	CP	H
	JP	Z,PCTLS1	;if at end, restore auxiliary buffer
;
;
; See if the output has caught up with the input - if so, reset the
; pointers to the start of the buffer
;
	CALL	CMP$I$O
	RET	NZ		;if not, back to work
	LD	HL,PBUFF	;if output caught input, reset both
	LD	(HLSAV1),HL	;  to bottom of buffer to start over
	LD	(HLSAV2),HL
	RET
;.....
;
;
; Compare the input and output pointers to see if the same address
;
CMP$I$O:LD	HL,(HLSAV1)	;get input pointer address
	EX	DE,HL		;put in 'DE'
	LD	HL,(HLSAV2)	;get output pointer address
	LD	A,H
	CP	D
	RET	NZ		;return if different
	LD	A,L
	CP	E
	RET
;.....
;
;
GTMAX:	LD	A,(SAVCCP)	;going to save 'CCP'?
	OR	A
	LD	A,(BDOS+2)	;'MSP' of 'BDOS' address
	JP	Z,GTMAX1
	SBC	A,8		;'CCP' is 2k or 8 pages
;
GTMAX1:	LD	(MAXRAM),A	;save
	RET
;.....
;
;
; This subroutine will loop until the modem receives a character or 100
; milliseconds.  It returns with a character in 'A' reg. but if no char-
; acter was recieved it returns after a timeout with carry set.
;
INMDM:	PUSH	HL
	LD	HL,63		;about 100 milliseconds
	CALL	FIXCNT
	LD	B,H		;delay is in 'HL'
	LD	C,L		;transfer to 'BC'
	POP	HL		;get original value of 'HL' back
;
INMDM1:	CALL	RCVRDY		;see if there is a character ready
	JP	NZ,INMDM2	;if no character, exit
	CALL	I$MDDATP	;get the character
	AND	7FH		;strip off any parity
	RET			;return with character in 'A' reg.
;...
;
;
INMDM2:	DEC	BC		;otherwise keep timing
	LD	A,B
	OR	C
	JP	NZ,INMDM1	;loop until timeout if needed
	SCF			;shows a timeout occured
	RET
;.....
;
;
; Send a space tone to the phone line for a short time.
;
BREAK:	LD	A,(PMMIMD)	;using the PMMI modem?
	OR	A
	JP	Z,BREAK1	;if not, exit
	LD	A,(MDCTLB)	;get the last modem control byte
	AND	BRKMASK		;set the transmit break bit low
	CALL	O$MDCTL2	;send it to the modem
	LD	B,2
	CALL	TIMER		;send a space tone for 200 ms.
	LD	A,(MDCTLB)	;get the last modem control byte
	CALL	O$MDCTL2	;restore to normal
	JP	TERM		;back to work
;...
;
;
BREAK1:	CALL	J$BREAK		;get the user'S BREAK ROUTINE
	JP	TERM		;back to work
;.....
;
;
;=======================================================================
;		     WRITE BUFFER TO DISK
;
; Make sure this record is included in the count.
;
WRDSK:	LD	HL,(HLSAV)
	LD	(HL),EOFCHAR	;ASCII file, store end-of-file char.
	LD	DE,127
	ADD	HL,DE
;
WRDSK1:	LD	DE,-(BUFFER)	;subtract the start of the buffer
	ADD	HL,DE		;by adding a minus number to buffer end
	LD	A,L		;divide hl by 128
	OR	A
	RLA			;  to get the
	LD	L,H		;  number of records
	LD	H,0
	PUSH	AF
	ADD	HL,HL
	POP	AF
	LD	A,0
	ADC	A,L
	LD	L,A		;number of records in 'HL'
;
;
; See if buffer is empty.  If yes, see if we need to erase an empty
; file or have already written something.
;
	LD	DE,BUFFER
	LD	A,(DE)
	CP	EOFCHAR		;'EOF' in first address means
	JP	NZ,WRDSK2	;  nothing in buffer to write
	LD	A,(WRFLG)	;first time by this way?
	OR	A
	JP	Z,NOWRITE	;if yes, show erasing file
	RET			;otherwise go close file
;.....
;
;
; Write to disk.  Start from BUFFER (in 'DE').	Number of records to
; write in 'HL'.
;
WRDSK2:	LD	C,STDMA
	CALL	BDOSRT
	PUSH	DE
	LD	C,WRITE
	LD	DE,FCB3		;location of filename to write to
	CALL	BDOSRT
	POP	DE
	OR	A
	JP	NZ,WRERRSP	;error if disk is full ** special patch
	EX	DE,HL		;put the current address in 'HL'
	PUSH	DE		;  and number of records left in
	LD	DE,128		;  for now
	ADD	HL,DE		;add for next record write, now in 'HL'
	POP	DE		;restore number of records left
	EX	DE,HL		;records to 'HL' again, address to 'DE'
	DEC	HL		;one less record left
	LD	A,H
	OR	L		;done writing when 'H' and 'H' both zero
	JP	NZ,WRDSK2	;otherwise do another disk write
	RET
;.....
;
;
; Error while writing a record, show why it is aborting
;
WRERR:	LD	C,CANCEL	;send cancel char. to sending station
	CALL	SND
	CALL	CLOSFIL		;close the current file
;
WRERR1:	CALL	ERXIT		;also will reset stack
	DEFB	'++ DISK FULL, SAVING PARTIAL FILE ++','$'
;.....
;
;
; If no data to store on the disk, close the empty file and erase it
;
NOWRITE:CALL	WRFIL2		;close the empty file
	CALL	NOASK		;erase the empty file
	CALL	ILPRT
	DEFB	'++ Nothing to save, erasing file ++'
	DEFB	CR,LF,BELL,0
	JP	DONETA		;reset any flags, return to menu
;.....
;
;
; Show you are in memory-save for disk write
;
TYPSEM:	LD	A,';'
	JP	TYPE		;show on CRT, return
;.....
;
;
; Save the registers, call BDOS then restore the registers
;
BDOSRT:	PUSH	BC
	PUSH	DE
	PUSH	HL
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	RET
;.....
;
;
INITFCB:LD	(HL),0		;entry at +2 will leave drive # intact
	INC	HL		;will initialize an FCB
	LD	B,11		;pointed to by HL-reg. fills 1st pos.
;
LOOP11:	LD	(HL),' '	;  with 0, next 11 with
	INC	HL		;  with blanks, and last
	DEC	B		;  21 with nulls
	JP	NZ,LOOP11
	LD	B,21
;
LOOP21:	LD	(HL),0
	INC	HL
	DEC	B
	JP	NZ,LOOP21
	RET
;.....
;
;
; Scans CMDBUF counting names and putting delimiter (space) after last
; name.
;
SCAN:	PUSH	HL
	LD	HL,NAMECT
	LD	(HL),0
	LD	HL,CMDBUF+1	;find end of command line, add space
	LD	C,(HL)
	LD	B,0
	LD	HL,CMDBUF+2
	ADD	HL,BC
	LD	(HL),' '
	LD	HL,CMDBUF+1
	LD	B,(HL)
	INC	B
	INC	B
;
SCANL1:	INC	HL
	DEC	B
	JP	Z,DNSCAN
	LD	A,(HL)
	CP	' '
	JP	NZ,SCANL1
;
SCANL2:	INC	HL		;eat extra spaces
	DEC	B
	JP	Z,DNSCAN
	LD	A,(HL)
	CP	' '
	JP	Z,SCANL2
	LD	(BGNMS),HL	;save start of names in CMDBUF
	INC	B
	DEC	HL
;
SCANL3:	INC	HL
	DEC	B
	JP	Z,DNSCAN
	LD	A,(HL)
	CP	' '
	JP	NZ,SCANL3
	LD	A,(NAMECT)	;counts names
	INC	A
	LD	(NAMECT),A
;
SCANL4:	INC	HL		;eat spaces
	DEC	B
	JP	Z,DNSCAN
	LD	A,(HL)
	CP	' '
	JP	Z,SCANL4
	JP	SCANL3
;.....
;
;
DNSCAN:	LD	(HL),' '	;space after last character
	POP	HL
	RET
;.....
;
;
; Places next name in buffer so 'CMDLINE' may parse it
;
TRTOBUF:LD	HL,(BGNMS)
	LD	B,0
	LD	DE,FCBBUF+2
;
TBLP:	LD	A,(HL)
	CP	' '
	JP	Z,TRBFEND
	LD	(DE),A
	INC	HL
	INC	DE
	INC	B		;count characterss in name
	JP	TBLP
;.....
;
;
TRBFEND:INC	HL
	LD	A,(HL)		;eat extra spaces
	CP	' '
	JP	Z,TRBFEND
	LD	(BGNMS),HL
	LD	HL,FCBBUF+1	;put # chars before name
	LD	(HL),B
	RET
;.....
;
;
CKCPM2:	LD	C,CPMVER	;BDOS 12 -- version number -- cp/m 2.x?
	CALL	BDOS
	OR	A
	RET	Z
	LD	C,STDMA
	LD	DE,TBUF
	CALL	BDOS
	LD	C,SRCHF
	LD	DE,FCB
	CALL	BDOS
	CP	0FFH
	RET	Z
;
	CALL	GETADD
	LD	DE,9
	ADD	HL,DE		;point to R/O attribute byte
	LD	A,(HL)
	AND	80H		;test most significant byte
	JP	NZ,MKCHG	;if set, make change
	INC	HL		;check system attribute byte
	LD	A,(HL)
	AND	80H
	RET	Z		;not $SYS or $R/O attribute
	DEC	HL
;
MKCHG:	LD	DE,-8
	ADD	HL,DE		;point HL to filename + 1
	LD	DE,FCB+1	;move directory name to FCB
	LD	B,11		;  without changing drive
	CALL	MOVE
	LD	HL,FCB+9	;R/O attribute
	LD	A,(HL)
	AND	7FH		;strip R/O attribute
	LD	(HL),A
	INC	HL		;system attribute
	LD	A,(HL)
	AND	7FH
	LD	(HL),A
	LD	DE,FCB
	LD	C,30		;set new attributes in directory
	CALL	BDOS
;
;
; Called by 'CKBAKUP' below, return done here through 'BDOS' jump
;
PLANCHG:LD	HL,FCB		;change name to type "BAK"
	LD	DE,FCB2
	LD	B,9		;move drive and name (not type)
	CALL	MOVE
	LD	HL,75H		;start of type in FCB2
	LD	(HL),'B'
	INC	HL
	LD	(HL),'A'
	INC	HL
	LD	(HL),'K'
	LD	DE,FCB2
	LD	C,ERASE		;erase any previous backups
	CALL	BDOS
	LD	HL,FCB2		;FCB2 drive field should have
	LD	(HL),0
	LD	DE,FCB
	LD	C,23		;rename
	JP	BDOS
;.....
;
;
CKBAKUP:LD	A,(BACKUP)
	OR	A
	RET	Z
	LD	C,SRCHF
	LD	DE,FCB
	CALL	BDOS
	INC	A
	RET	Z		;file not found
	JP	PLANCHG		;in 'CKCPM2' - return done there
;.....
;
;
;***********************************************************************
;
;	    RECEIVE A RECORD FROM SNDING STATION
;
;***********************************************************************
;
; If CRC is in effect, there is a 10-second timeout to the first SOH.
; It then tries six more times to let the sender know the system is
; capable of receiving a 'CRC' check.  At the end of that time a NAK is
; sent which tells the sender to use CHECKSUM checking instead of CRC.
; This allows automatic compatability with systems implementing CRC -
; (Cyclic Redundancy Checking).  The search for SOH will cycle through
; one record interval and ignore noise or characters sent by the remote
; for any purpose (such as progress reporting).  So extraneous characters
; that are sometimes sent by remote-end protocol will be gobbled up until
; the first SOH.  EOT is tested only as the first returned character af-
; ter each sector.
;
SRCHSOH	EQU	160		;number of times to loop search for SOH
;
RCVRECD:LD	A,1
	LD	(ERRCT),A	;initialize the error count
;
RCVSQ:	LD	B,10		;10 seconds allowed to receive 1st char.
	LD	DE,SRCHSOH	;initialize loof for up to 160 seconds
	CALL	RECV		;get the 1st character
	JP	C,RCVSTOT	;timeout error if not rcvd in 10 seconds
	LD	C,A		;save the character for now
	CP	EOT		;see if end of transmission
	SCF			;set carry
	RET	Z		;return with carry set
;
SOHLUP:	LD	A,0FFH
	LD	(CHRFLG),A
	LD	(TIMFLG),A
	LD	A,E		;get search count-down value
	CP	SRCHSOH		;see if it is the 1st returned character
	LD	A,C		;get the first character now
	JP	Z,NORECV	;skip RECV routine if 1st character
	LD	B,1
	CALL	RECV
	LD	B,A
	JP	NC,TSTSOH
;
NORECV:	LD	B,A
	XOR	A		;else set the value that forces timeout
	LD	(CHRFLG),A
;
TSTSOH:	LD	A,B		;get the character
	CP	SOH		;see if it is SOH
	PUSH	AF
	XOR	A
	LD	(TIMFLG),A	;restore this flag
	POP	AF
	JP	Z,RCVSOH	;got SOH, get rcd # and its complement
	LD	A,D
	OR	E		;see if counted-down to zero
	DEC	DE
	JP	NZ,SOHLUP	;go around again if not
	LD	A,(CHRFLG)	;see if timeout needs to be forced
	OR	A
	JP	Z,RCVSTOT	;go do timeout and countthem
	LD	A,(QFLG)
	OR	A
	JP	Z,RCVSRR
;
RCVSH:	CALL	CRLF
	LD	A,B
	CALL	HEXO
	CALL	ILPRT
	DEFB	'H received not SOH - ',0
;
RCVPRN:	CALL	SHOERR		;display error count
;
RCVSRR:	CALL	WAITQ1		;wait for 1 second with no characters
	CALL	CKABORT		;want to stop receiving now?
	LD	A,(CRCFLAG)	;get 'CRC' flag
	OR	A		;'CRC' in effect?
	LD	A,NAK		;put 'NAK' in 'A' register
	JP	Z,RCVSR1	;no, send the 'NAK'
	LD	A,(FIRSTME)	;get first time switch
	OR	A		;has first 'SOH' been received?
	LD	A,NAK		;put 'NAK' in 'A' register
	JP	NZ,RCVSR1	;yes, then send 'NAK'
	LD	A,CRC		;tell sender 'CRC' is in effect
;
RCVSR1:	CALL	SND		;  the 'NAK' or 'CRC' request
	LD	A,(ERRCT)	;abort if we have reached error limit
	INC	A
	LD	(ERRCT),A	;store for next time
	CP	ERRLIM		;see if at limit yet
	JP	C,RCVSQ		;if not, keep going
	LD	A,(RETRY)	;see if retry after 10 errors is set
	OR	A
	JP	Z,ABORT		;if 'YES', abort
	JP	CKQIT		;if 'NO' check for continued use
;
RCVSABT:LD	SP,STACK	;reset the stack just in case
	CALL	CLOSFIL		;close the partial file
	CALL	NOASK		;delete partial file
	CALL	ILPRT
	DEFB	CR,LF,LF
	DEFB	'++ RECEIVED FILE CANCELLED ++',CR,LF,BELL
	DEFB	'++ UNFINISHED FILE DELETED ++',CR,LF,0
	JP	DONETA
;
RCVSTOT:LD	A,(QFLG)
	OR	A
	JP	Z,RCVSCC
;
RCVSPT:	CALL	ILPRT
	DEFB	CR,LF,'++ Timeout ',0
	CALL	SHOERR
;
RCVSCC:	CALL	RCVSCC2
	JP	RCVSRR
;
;
; Routine will switch from 'CRC' to Checksum if 'ERCNT' reaches 'ERRCRC'
; and 'FIRSTIME' is false.
;
RCVSCC2:LD	A,(ERRCT)
	CP	ERRCRC
	RET	NZ
	LD	A,(FIRSTME)
	OR	A
	RET	NZ
	LD	A,(CRCFLAG)
	OR	A
	RET	Z
	CPL
	LD	(CRCFLAG),A
	LD	(CRCDFLT),A
	CALL	ILPRTQ
	DEFB	'** Switching to Checksum mode **',CR,BELL,LF,0
	RET
;.....
;
;
; Got SOH - get block #, block # complemented
;
RCVSOH:	LD	A,0FFH
	LD	(FIRSTME),A	;indicate 1st soh was received
	LD	B,5		;timeout = 5 seconds
	CALL	RECV		;get record
	JP	C,RCVSTOT	;got timeout
	LD	D,A
	LD	B,5		;timeout = 5 seconds
	CALL	RECV
	JP	C,RCVSTOT
	CPL
	CP	D
	JP	Z,RCVDATA
	LD	A,(QFLG)
	OR	A
	JP	Z,RCVSRR
;
RCVBSE:	CALL	ILPRT
	DEFB	CR,LF,'++ Bad record # in header ',0
	JP	RCVPRN
;
RCVDATA:LD	A,D
	LD	(RCVRNO),A
	LD	A,1
	LD	(DATAFLG),A
	LD	C,0
	LD	HL,0
	LD	(CRCVAL),HL
	LD	HL,80H
;
RCVCHR:	LD	B,5		;wait up to 5 seconds for a character
	CALL	RECV
	JP	C,RCVSTOT
	LD	(HL),A
	INC	L
	JP	NZ,RCVCHR
	XOR	A
	LD	(DATAFLG),A
	LD	A,(CRCFLAG)
	OR	A
	JP	NZ,RCVCR
	LD	D,C
	LD	B,5		;wait up to 5 seconds for an answer
	CALL	RECV
	JP	C,RCVSTOT
	CP	D
	JP	NZ,RCVCERR
;
CHKSNUM:LD	A,(RCVRNO)
	LD	B,A
	LD	A,(RECNO)
	CP	B
	JP	Z,RCVACK
	INC	A
	CP	B
	JP	NZ,ABORT
	RET
;.....
;
;
RCVCR:	LD	E,2		;number of 'CRC' bytes
;
RCVCR2:	LD	B,5		;wait up to 5 seconds for a character
	CALL	RECV
	JP	C,RCVSTOT
	DEC	E
	JP	NZ,RCVCR2
	CALL	CRCCHK
	OR	A
	JP	Z,CHKSNUM
	LD	A,(QFLG)
	OR	A
	JP	Z,RCVSRR
;
RCVCRER:CALL	ILPRT
	DEFB	'++ CRC error ',0
	JP	RCVPRN
;
RCVCERR:LD	A,(QFLG)
	OR	A
	JP	Z,RCVSRR
;
RCVCPR:	CALL	ILPRT
	DEFB	'++ CHECKSUM error ',0
	JP	RCVPRN
;
RCVACK:	CALL	SNDACK
	JP	RCVRECD
;
;
; Get the error count and display on CRT
;
SHOERR:	PUSH	HL
	LD	HL,(ERRCT)
	LD	H,0
	CALL	DECOUT
	POP	HL
	CALL	ILPRT
	DEFB	' ++',CR,LF,0
	LD	A,(ERRCT)
	CP	ERRLIM
	JP	NC,ABORT
	RET
;.....
;
;
SNDHDR:	LD	A,(QFLG)
	OR	A
	JP	Z,SNDHNM
	CALL	ILPRT
	DEFB	CR,'Sending # ',0
	PUSH	HL		;store current address
	LD	HL,(RECNO)	;get record number
	CALL	DECOUT		;print it in decimal
	CALL	ILPRT
	DEFB	' ',0
;
	LD	A,(HEXSHO)
	OR	A
	JP	Z,SNDHNM-1
	CALL	ILPRT
	DEFB	'(',0
	CALL	DHXOUT		;16 bit hex conversion & output
	CALL	ILPRT
	DEFB	'H) ',0
;
	POP	HL		;restore current address
;
SNDHNM:	LD	A,SOH		;send 'SOH' character to the output
	CALL	SND
	LD	A,(RECNO)	;send record number to the output
	CALL	SND
	LD	A,(RECNO)
	CPL			;complement the record number
	JP	SND		;send this value to the output
;.....
;
;
SNDREC:	LD	A,1
	LD	(DATAFLG),A
	LD	C,0
	LD	HL,0		;new record, clear 'CHECKSUM' value
	LD	(CRCVAL),HL	;new record, clear 'CRC' value
	LD	HL,TBUF		;store at 0080H
;
SNDC:	LD	A,(HL)
	CALL	SND
	INC	L
	JP	NZ,SNDC
	XOR	A
	LD	(DATAFLG),A
	RET
;.....
;
;
SNDCKS:	LD	A,C
	JP	SND
;.....
;
;
SNDCRC:	PUSH	HL
	LD	HL,(CRCVAL)
	LD	A,H
	CALL	SND
	LD	A,L
	CALL	SND
	POP	HL
	XOR	A		;reset the carry bit
	RET
;.....
;
;
; After a record is sent, a character is returned telling if it was re-
; ceived properly or not.  An ACK allows the next record to be sent.  A
; NAK causes the current record to be resent.  If no character (or any
; character other than ACK or NAK) is received after a short wait (10
; to 12 seconds), a timeout error message is shown and the record will
; be resent.  The GTACK routine can gobble up a string of up to 191
; characters while searching for an 'ACK' or a 'NAK'.
;
GTACK:	LD	E,192		;number of characters to gobble
;
ACKLUP:	LD	A,0FFH
	LD	(CHRFLG),A	;set the character flag
	LD	(TIMFLG),A	;set the time flag
	LD	B,1
	CALL	RECV
	LD	B,A		;save the character
	JP	NC,ACKTST
	XOR	A
	LD	(CHRFLG),A	;reset the character flag, was none
;
ACKTST:	XOR	A
	LD	(TIMFLG),A
	LD	A,B		;get the character back
	CP	ACK
	RET	Z
	CP	NAK
	JP	Z,GTACK1
;
NOAKNK:	DEC	E		;one less to go
	JP	NZ,ACKLUP	;loop around again if not zero
	LD	A,(CHRFLG)
	OR	A
	JP	Z,GETATOT
;
GTACK1:	LD	A,(BENHERE)
	XOR	B
	JP	Z,ACKER0	;do not say 'ACK error' if 1st 'NAK'
	LD	A,(QFLG)
	OR	A
	JP	Z,ACKER
	CALL	ILPRT
	DEFB	'++ ',0
	LD	A,B
	CP	NAK
	JP	Z,GTACK3
	CALL	HEXO
	CALL	ILPRT
	DEFB	'H',0
	JP	GTACK4
;
GTACK3:	CALL	ILPRT
	DEFB	'NAK',0
;
GTACK4:	CALL	ILPRT
	DEFB	' received not ACK - ',0
	CALL	SHOERR
;
ACKER0:	XOR	A
	LD	(BENHERE),A
;
ACKER:	LD	A,(ERRCT)
	INC	A
	LD	(ERRCT),A
	CP	ERRLIM+1	;at error limit yet?
	RET	C		;if not, return
;
ACKER1:	CALL	ERXIT
	DEFB	CR,LF,'++ SEND-FILE CANCELLED ++','$'
;.....
;
;
; Reached error limit
;
GETATOT:CALL	ILPRT
	DEFB	CR,'++ TIMEOUT - no ACK - ',0
	CALL	SHOERR		;display error count
	JP	ACKER
;.....
;
;
CKABORT:LD	A,(QFLG)
	OR	A
	RET	Z
	CALL	STAT
	RET	Z
	CALL	KEYIN
	CP	CANCEL
	RET	NZ
;
;
; Aborts send or receive routines and returns to command line
;
ABORT:	LD	SP,STACK
;
ABORTL:	LD	B,1		;1-second delay to clear input
	CALL	RECV
	JP	NC,ABORTL
	LD	A,CANCEL	;show you are cancelling
	CALL	SND
;
ABORTW:	LD	B,1		;1-second delay to clear input
	CALL	RECV
	JP	NC,ABORTW
	LD	A,' '
	CALL	SND
	LD	A,'B'		;turn multi-file mode
	LD	(BCHFLG),A	;  off so routine ends
	LD	(ABTFLG),A	;shows an abort was made
	XOR	A
	LD	(NFILFLG),A	;stop copy into memory for disk file
	LD	A,(OPTION)	;receiving a file now?
	CP	'R'
	JP	Z,RCVSABT	;if yes, cancel the unfinished file
	CALL	ILPRT
	DEFB	CR,LF,LF,'++ FILE CANCELLED ++',CR,LF,BELL,0
	JP	DONETA
;.....
;
;
; Increment the record count
;
INCRRNO:PUSH	HL
	LD	HL,(RECNO)	;get record number
	INC	HL		;bump it
	LD	(RECNO),HL	;store it
	LD	A,L
	POP	HL
	RET
;.....
;
;
; First check for any wild cards and disallow, just to be safe.  Do not
; want a group of files being accidently erased.
;
ERASF:	LD	HL,FCB		;file name is stored here
	LD	B,11		;maximum of 11 chars for filename.ext
;
ERASF1:	INC	HL		;next location in file name
	LD	A,(HL)		;get the char.
	CP	'?'		;check for any wild card characters
	JP	Z,ERRORW	;error if one is found
	DEC	B		;number of tries left
	JP	NZ,ERASF1	;if not zero, keep checking
	LD	A,(BCHFLG)	;do not ask for erase
	OR	A		;  in multi-file mode,
	JP	Z,NOASK		;  just do it
	LD	DE,FCB
	LD	C,SRCHF
	CALL	BDOS
	INC	A
	RET	Z		;file erased ok, return
	CALL	ILPRT		;otherwise make sure it is ok
	DEFB	'File exists - erase?  (Y/N): ',BELL,0
	CALL	KBDCHR
	CP	'Y'
	JP	NZ,MENU		;if not a 'Y' do not erase
	CALL	CRLF		;otherwise erase the file
;
NOASK:	LD	DE,FCB
	LD	C,ERASE
	JP	BDOS
;.....
;
;
ERRORW:	POP	HL		;restore stack from "call ERASF"
	CALL	ILPRT
	DEFB	'++ NO WILDCARDS ALLOWED FOR TEXT FILES ++'
	DEFB	CR,LF,BELL,0
	JP	MENU
;.....
;
;
BLKFILE:CALL	ILPRT		;no file named for send or receive
	DEFB	'++ NO FILE SPECIFIED ++',CR,LF,BELL,0
	JP	MENU
;.....
;
;
MAKEFIL:LD	DE,FCB
	LD	C,MAKE
	CALL	BDOS
	INC	A
	RET	NZ
	CALL	ERXIT
	DEFB	'++ ERROR -- Can''t open file ++',CR,LF
	DEFB	'++ Directory is perhaps full ++','$'
;
CNREC:	LD	C,FILSIZ	;compute file size function in CP/M 2.x
	LD	DE,FCB		;point to file control block
	CALL	BDOS
	LD	HL,(FCB+33)	;get record count
	LD	(RCNT),HL	;store it
	LD	HL,0		;zero 'HL'
	LD	(FCB+33),HL	;reset random record in FCB
	RET
;.....
;
;
OPENFIL:XOR	A
	LD	(FCBEXT),A
	LD	DE,FCB
	LD	C,OPEN
	CALL	BDOS
	INC	A
	JP	NZ,SNDTM	;send transfer time, # of records, etc.
	CALL	ERXIT		;file did not open
	DEFB	'++ FILE NOT FOUND ++','$'
;.....
;
;
CLOSFIL:LD	DE,FCB		;get the file name
	LD	C,CLOSE
	CALL	BDOS		;close the file
	INC	A
	RET	NZ
	JP	ERXIT1		;no file to close, exit
;.....
;
;
; Update record read
;
RDRECD:	LD	A,(RECINBF)	;decrement 'RECORDS IN BUFFER' count
	DEC	A
	LD	(RECINBF),A
	JP	M,RDBLOCK
	LD	HL,(RECPTR)	;find where last move stopped
	LD	DE,128
	CALL	MOVE128		;move 128 characters
	LD	(RECPTR),HL	;store new address for next move
	RET
;.....
;
;
; Buffer empty so read in another block from the disk
;
RDBLOCK:LD	A,(EOFLG)
	CP	1
	SCF
	RET	Z
	LD	C,0
	LD	DE,BUFFER
;
RDRECLP:PUSH	BC
	PUSH	DE
	LD	C,STDMA
	CALL	BDOS
	LD	DE,FCB
	LD	C,READ
	CALL	BDOS
	POP	DE
	POP	BC
	OR	A
	JP	Z,RDRECOK
	DEC	A
	JP	Z,REOF
	CALL	ERXIT
	DEFB	'++ FILE READ ERROR ++','$'
;
RDRECOK:LD	HL,128
	ADD	HL,DE
	EX	DE,HL
	INC	C
	CALL	DSKSIZ		;establish buffer size
	JP	Z,RDBFULL
	JP	RDRECLP
;...
;
;
REOF:	LD	A,1
	LD	(EOFLG),A
	LD	A,C
;
;
; Buffer full or received "End Of File (EOF)"
;
RDBFULL:LD	(RECINBF),A
	LD	HL,BUFFER
	LD	(RECPTR),HL
	LD	C,STDMA
	LD	DE,TBUF
	CALL	BDOS
	JP	RDRECD
;.....
;
;
; Write a record
;
WRRECD:	LD	HL,(RECPTR)
	EX	DE,HL
	LD	HL,128
	CALL	MOVE128
	EX	DE,HL
	LD	(RECPTR),HL	;new record pointer
	LD	A,(RECINBF)	;increment 'RECORDS IN BUFFER' count
	INC	A
	LD	(RECINBF),A
	LD	C,A		;store the record count for now
	CALL	DSKSIZ		;establish buffer size
	RET	NZ		;buffer not full, return
;
;
; Write a block to disk
;
WRBLOCK:LD	A,(RECINBF)	;get the number of records in the buffer
	OR	A
	RET	Z		;if zero, don't try to move to disk
	LD	C,A		;otherwise store in 'C' register
	LD	DE,BUFFER	;start of buffer to move to disk
;
DSKWRT:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,STDMA
	CALL	BDOS
	LD	C,WRITE
	LD	DE,FCB
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	OR	A
	JP	NZ,WRERR	;error if disk is full
	LD	HL,128		;add in another page
	ADD	HL,DE
	EX	DE,HL
	DEC	C		;one less record left to move to disk
	JP	NZ,DSKWRT
	XOR	A
	LD	(RECINBF),A	;zero the 'RECORDS IN BUFFER' count
	LD	HL,BUFFER	;reset location to next buffer start
	LD	(RECPTR),HL
	RET
;.....
;
;
; Determine if the buffer size is for file transfer or for ASCII capture
; to disk then compare with current record length
;
DSKSIZ:	LD	A,(XFLG)	;see if transferring files now
	OR	A
	LD	A,C		;get the current record count
	JP	Z,DSKSIZ1	;if yes, exit
	LD	A,C
	CP	BUFSIZ*8	;buffer size for ASCII capture to disk
	RET			;return with flag set for the compare
;...
;
;
DSKSIZ1:LD	A,(SAVSIZ)	;get the file transfer buffer size..
	CP	C		;  from special storage area and compare
	RET			;return with flag set for the compare
;.....
;
;
; Timeout time is in B, in seconds.  Entry via 'RECVDG' deletes garbage
; characters on the line.  For example, having just sent a record, cal-
; ling RECVDG will delete any line noise induced characters LONG before
; the ACK/NAK would be received.
;
RECVDG:	CALL	CKCHAR		;catch any garbage characters
;
RECV:	PUSH	DE
;
;
; Get back quickly to gobble 2nd character if TIMFLG is set by the GETNM
; routine - or just step through quickly after the first wait for 'SOH'
; in the 'SOHLUP' routine.
;
MSEC:	PUSH	HL
	LD	HL,TIMFLG
	LD	E,(HL)
	INC	E
	LD	HL,(QUIKTIM)
	JP	Z,DOQUIK
	LD	HL,(TIMVAL)
;
DOQUIK:	EX	DE,HL
	POP	HL
;
MWTI:	CALL	RCVRDY
	JP	Z,MCHAR
	LD	A,D
	OR	E
	DEC	DE
	JP	NZ,MWTI
	DEC	B
	JP	NZ,MSEC
	POP	DE
	CALL	CKABORT
	SCF
	RET
;.....
;
;
; Get the character from the modem, but filter out 'ACK' and '.' chars.
; if recieving a file name.  ('FILTRFLG' is set by the 'GETNM' routine.)
;
MCHAR:	CALL	I$MDDATP	;get the character that is waiting
	POP	DE
	PUSH	AF		;save the character for later use also
	CP	ACK		;see if it is 'ACK'
	JP	Z,ISACK
	CP	'.'		;see if it is a period
	JP	NZ,DOUPD	;neither, so update 'CRC'
;
ISACK:	PUSH	HL
	PUSH	DE
	LD	HL,FLTRFLG	;see if need to each 'ACK' or period
	LD	E,(HL)
	INC	E
	POP	DE
	POP	HL
	JP	Z,MWTI		;yes, so do it
;
DOUPD:	CALL	CRCUPD		;calculate 'CRC'
	ADD	A,C
	LD	C,A
	LD	A,(RSEEFLG)
	OR	A
	JP	Z,MONIN
	LD	A,(VSEEFLG)
	OR	A
	JP	NZ,NOMONIN
	LD	A,(DATAFLG)
	OR	A
	JP	Z,NOMONIN
;
MONIN:	POP	AF		;get the character again
	PUSH	AF		;resave it for later use also
	CALL	SHOW		;show the character on the CRT
;
NOMONIN:CALL	CKABORT
	POP	AF		;get the character back once more
	OR	A		;reset the carry flag
	RET			;return with the character and flag set
;.....
;
;
; Send a character to the modem
;
SND:	PUSH	AF
	LD	A,(SSEEFLG)
	OR	A
	JP	Z,MONOUT
	LD	A,(VSEEFLG)
	OR	A
	JP	NZ,NOMONOT
	LD	A,(DATAFLG)
	OR	A
	JP	Z,NOMONOT
;
MONOUT:	POP	AF
	PUSH	AF
	CALL	SHOW
;
NOMONOT:POP	AF
	PUSH	AF
	CALL	CRCUPD		;update the 'CRC' calcuation
	ADD	A,C
	LD	C,A
;
SNDW:	CALL	SNDRDY
	JP	NZ,SNDW
	POP	AF
	JP	O$MDDATP	;send character to modem, done
;.....
;
;
; Waits for the first character received while waiting to send a file.
; If a character is not received in one second, it loops again until a
; char. is received or it times out.  The count is set for two minutes
; before timeout.  This gives the receiving station ample time to name
; a file, etc.
;
WAITNAK:CALL	ILPRT
	DEFB	'Waiting ready signal',CR,LF,0
	CALL	CRLF
;
WAITNLP:CALL	CKABORT
	LD	B,1		;wait up to 1 second for a character
	CALL	RECV
	CP	CANCEL		;want to quit?
	JP	Z,ABORT
	CP	CRC		;'CRC' request?
	JP	Z,WAITCRC	;yes, go set 'CRC' flag
	CP	NAK
	JP	Z,WAICK
	DEC	E
	JP	NZ,WAITNLP
	JP	ABORT
;...
;
;
WAITCRC:CALL	ILPRTQ
	DEFB	'CRC request received',CR,LF,0
	LD	A,1
	LD	(CRCFLAG),A	;make sure in 'CRC' mode then
	RET
;.....
;
;
WAICK:	LD	A,(BCHFLG)	;in batch mode?
	OR	A
	RET	Z
	CALL	ILPRTQ
	DEFB	'Got checksum request',CR,LF,0
	RET
;.....
;
;
WAICK1:	CALL	ILPRTQ
	DEFB	'Name NAK received',CR,LF,0
	RET
;.....
;
;
; Finished with the file transfer
;
DONE:	LD	A,(BCHFLG)	;in batch mode?
	OR	A
	JP	NZ,DONET	;exit if not
	LD	A,(QFLG)
	OR	A
	JP	Z,NMSTRNS
	LD	B,12		;zero out FTRNM
	LD	HL,FTRNM
	LD	A,0
;
ZEROLP:	LD	(HL),A
	INC	HL
	DEC	B
	JP	NZ,ZEROLP
	LD	B,12		;put file name in FTRNM
	LD	HL,FCB+1
	LD	DE,FTRNM
;
LOADMSG:LD	A,4		;start of file type?
	CP	B
	JP	Z,PERIOD	;put in period if so
	LD	A,(HL)
	CP	' '
	JP	Z,SKPSP
	LD	(DE),A		;store in FTRNM
	INC	DE
;
SKPSP:	INC	HL
	DEC	B
	LD	A,B
	OR	A		;end of file name?
	JP	Z,FTRNM0	;display file name
	JP	LOADMSG		;loop for another character
;.....
;
;
PERIOD:	LD	A,(HL)
	CP	' '		;is file type empty?
	JP	Z,FTRNM0	;go if so
	LD	A,'.'		;else put period in message
	LD	(DE),A
	INC	DE
	DEC	B
	JP	LOADMSG
;.....
;
;
FTRNM0:	CALL	ILPRT
	DEFB	CR,LF
;
FTRNM:	DEFS	12
	DEFB	0
	CALL	ILPRT
	DEFB	' Transferred',CR,LF,LF,BELL,0
;
NMSTRNS:LD	A,(FCB)		;save drive number
	LD	(DISKNO),A
	LD	HL,FCB		;blank out file control blocks
	CALL	INITFCB
	LD	A,(DISKNO)	;put drive number back
	LD	(FCB),A
	LD	HL,RESTSN	;restore record numbers
	LD	DE,RECNOB	;  for new file transfer
	LD	B,RECNOE-RECNOB	;routine also done in menu
	CALL	MOVE
	CALL	SNDNOW		;insures last character is finished
	CALL	CKCHAR		;catch any echo characters on line
	LD	A,(SNDFLG)	;goes to either send or
	OR	A		;  receive file, depending
	JP	NZ,SNDFL2	;  upon which routine set
	JP	RCVFL1		;  the flag in multi-file mode
;.....
;
;
DONET:	CALL	CKABORT		;slight delay for next message
	CALL	ILPRT
	DEFB	CR,LF,'[Transfer completed]',CR,LF,BELL,0
;
DONETA:	LD	A,(XITFLG)	;special 'X' flag set?
	OR	A
	JP	Z,BYEBYE	;if yes, disconnect and reboot
	LD	A,(DISCFLG)	;normal 'D' flag set?
	OR	A
	JP	Z,DONETD	;if yes, disconnect, get next command
;
DONETB:	CALL	J$NPARIT	;reset to no parity
	XOR	A
	LD	(CRCFLAG),A	;reset back to checksum
	LD	(FIRSTME),A	;reset first-time 'SOH' flag
	LD	(FSTFLG),A	;reset multi-file trans
	LD	(NFILFLG),A	;turn off the memory save for disk file
	LD	(SAVEFLG),A	;stop memory save in term routine.
	LD	A,(VSEEFLG)	;view flag set?
	OR	A
	JP	NZ,DONETC	;if not, exit
	CPL
	LD	(QFLG),A	;VSEEFLG also sets the QFLG
	LD	(VSEEFLG),A	;reset the flag
;
DONETC:	LD	HL,QFLG		;in quiet mode?
	LD	A,(HL)
	OR	A
	LD	(HL),'Q'	;reset the flag to normal
	JP	Z,MENU		;if yes, go back to command line
	LD	A,(ABTFLG)	;come here from a timeout?
	OR	A
	JP	NZ,MENU		;if yes, go to command mode
	LD	A,(JMPCMD)	;requesting return to command mode?
	OR	A
	JP	Z,MENU		;if yes go to command mode
	CALL	CRLF		;turn up a new line
	JP	TERM		;otherwise return to terminal mode
;.....
;
;
DONETD:	CALL	ILPRT
	DEFB	CR,LF,'<< DISCONNECTED >>',BELL,CR,LF,0
	CALL	J$GOODBY	;set 'DTR' low for 300 ms.
	LD	A,(PMMIMD)
	OR	A
;;;	CALL	NZ,J$GOODBY
	DEFB	0,0,0		;(PREVENT DOUBLE DISCONNECT)
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
;;;	CALL	NZ,J$GOODBY	;if yes, disconnect
	DEFB	0,0,0		;(PREVENT DOUBLE DISCONNECT)
	JP	MENU0		;back to command line
;.....
;
;
MOVEFCB:LD	HL,FCB+16
	LD	DE,FCB
	LD	B,16
	CALL	MOVE
	XOR	A
	LD	(FCBSNO),A
	LD	(FCBEXT),A
	RET
;.....
;
;
SHOW:	CP	LF
	JP	Z,CTYPE
	CP	CR
	JP	Z,CTYPE
	CP	9
	JP	Z,CTYPE
	CP	' '
	JP	C,SEEHEX
	CP	7FH
	JP	C,CTYPE
;
SEEHEX:	PUSH	AF
	LD	A,'('
	CALL	CTYPE
	POP	AF
	CALL	HEXO
	LD	A,')'
	JP	CTYPE
;.....
;
;
CTYPE:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,A
	LD	C,WRCON
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	RET
;.....
;
;
CRLF:	PUSH	AF
	LD	A,CR
	CALL	TYPE
	LD	A,LF
	CALL	TYPE
	POP	AF
	RET
;.....
;
;
STAT:	PUSH	BC
	PUSH	DE
	PUSH	HL
;
VSTAT:	CALL	$-$		;BIOS constat address, filled in
	POP	HL		;  by 'INITAD' routine
	POP	DE
	POP	BC
	OR	A
	RET
;.....
;
;
KEYIN:	PUSH	BC
	PUSH	DE
	PUSH	HL
;
VKEYIN:	CALL	$-$		;BIOS 'CONIN' address, filled in
	POP	HL		;  by 'INITAD' routine
	POP	DE
	POP	BC
	RET
;.....
;
;
TYPE:	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,A
;
VTYPE:	CALL	$-$		;BIOS 'CONOUT' address, filled in
	POP	HL		;  by 'INITAD' routine
	POP	DE
	POP	BC
	POP	AF
	RET
;.....
;
;
; Get a character from the keyboard, convert to upper-case if needed,
; and show on CRT
;
KBDCHR:	CALL	KEYIN		;get a keyboard character
	CALL	UCASE		;convert to upper case if needed
	CALL	TYPE		;show on CRT
	RET
;.....
;
;
UCASE:	CP	61H		;changes lower case character
	RET	C		;  in 'A'reg. to upper case
	CP	7AH+1		;see if more than small 'Z'
	RET	NC
	AND	5FH
	RET
;.....
;
;
DECOUT:	PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	BC,-10
	LD	DE,-1
;
DECOU1:	ADD	HL,BC
	INC	DE
	JP	C,DECOU1
	LD	BC,10
	ADD	HL,BC
	EX	DE,HL
	LD	A,H
	OR	L
	CALL	NZ,DECOUT
	LD	A,E
	ADD	A,'0'
	CALL	CTYPE
	POP	HL
	POP	DE
	POP	BC
	POP	AF
	RET
;.....
;
;
;---->	DHXOUT: - double precision hex output routine
;
DHXOUT:	PUSH	HL
	PUSH	AF
	LD	A,H		;get MS byte
	CALL	HEXO		;output high order byte
	LD	A,L		;get LS byte
	CALL	HEXO		;output low order byte
	POP	AF
	POP	HL
	RET
;.....
;
;
; Prints a hex value in 'A' on the CRT
;
HEXO:	PUSH	AF
	RRA
	RRA
	RRA
	RRA
	CALL	NIBBL
	POP	AF
;
NIBBL:	AND	0FH
	CP	10
	JP	C,ISNUM
	ADD	A,7
;
ISNUM:	ADD	A,'0'		;add in ASCII bias
	JP	CTYPE
;.....
;
;
; Displays the control-characters shown in the menu
;
SHFTYPE:PUSH	AF
	CALL	ILPRT
	DEFB	'CTL-',0
	POP	AF
	ADD	A,40H		;convert binary to ASCII chars.
	CALL	TYPE		;show on the CRT
	JP	ILPRT
;.....
;
;
; Write a string of characters to the CRT
;
ILPRT:	EX	(SP),HL
;
ILPRT1:	LD	A,(HL)		;get the character
	OR	A		;see if a "0" for end of string
	JP	Z,ILPRT2	;if yes, all done
	CALL	CTYPE		;show on CRT
	INC	HL		;get the next location in the string
	JP	ILPRT1
;
ILPRT2:	EX	(SP),HL		;restore the address
	RET
;.....
;
;
; Write a string of characters unless in the Quiet mode
ILPRTQ:	EX	(SP),HL
;
ILPRTQ1:LD	A,(HL)		;get the character
	OR	A		;see if a "0" for end of string
	JP	Z,ILPRTQ2	;if yes, all done
	LD	A,(QFLG)
	OR	A
	LD	A,(HL)
	CALL	NZ,CTYPE	;show on CRT if not in quiet mode
	INC	HL		;get the next location in the string
	JP	ILPRTQ1
;
ILPRTQ2:EX	(SP),HL		;restore the address
	RET
;.....
;
;
PRTMSG:	LD	C,PRINT		;print the string
	JP	BDOS
;.....
;
;
; Displays error statement then resturns to command mode
;
ERXIT:	POP	DE
	CALL	PRTMSG
	LD	A,BELL
	CALL	TYPE
	CALL	CRLF
;
ERXIT1:	LD	A,1
	LD	(ABTFLG),A	;shows an unintentional abort
	LD	A,(BCHFLG)	;in batch mode?
	OR	A
	JP	NZ,DONETB	;if not, exit
	JP	ABORT		;abort other computer
;.....
;
;
; Exits directly to CP/M, with no reboot unless you have selected pos-
; sible overwriting of 'CCP'
;
EXIT:	LD	A,(OLDUSER)	;get original user number back
	LD	E,A
	CALL	STUSER
	LD	C,STDMA
	LD	DE,TBUF		;restore original buffer area
	CALL	BDOS
	LD	BC,1A00H	;a little delay timer
;
EXIT1:	DEC	BC		;one less loop to make
	LD	A,B
	OR	C
	JP	NZ,EXIT1	;loop again till both are zero
	CALL	CKCON		;catch any extra keyboard characters
	LD	A,(NFILFLG)	;saving for a disk file?
	OR	A
	CALL	NZ,WRFIL1	;if yes, close the file
	LD	A,(SAVCCP)	;was 'CCP' left intact?
	OR	A
	JP	Z,0000H		;if not, warm reboot just in case
;
EXIT2:	XOR	A		;clear the 'A' reg. and all flags
	LD	HL,(STACK)	;get the original stack pointer back
	LD	SP,HL		;set the stack pointer to that address
	RET
;.....
;
;
; Catch any extra keyboard characters coming through BDOS
;
CKCON:	LD	C,CONST		;see if any characters waiting
	CALL	BDOS
	OR	A
	RET	Z		;if not, exit
	LD	C,RDCON		;otherwise get the character
	CALL	BDOS
	XOR	A		;discard the character
	JP	CKCON		;see if any others
;.....
;
;
MOVE128:LD	B,128
;
MOVE:	LD	A,(HL)
	LD	(DE),A
	INC	HL
	INC	DE
	DEC	B
	JP	NZ,MOVE
	RET
;.....
;
;
; Sends the character in 'A' to the modem
;
SNDCHR:	CALL	SNDNOW		;wait until modem is ready for character
	LD	A,B		;get the original character back
	JP	O$MDDATP	;send the character to modem, return
;.....
;
;
; Initializes CP/M file control blocks AT 5CH and 6CH
;
STFCB:	LD	DE,CMDBUF
	LD	HL,FCB
	JP	CMDLINE
;.....
;
;
; Adjusts loop counter for the selected clock speed.  Returns with delay
; in 'HL'.
;
FIXCNT:	LD	A,(CLOCK)	;get the user's clock speed
	PUSH	DE		;save the current 'DE' value
	PUSH	HL
	POP	DE		;get same value into 'DE' as in 'HL'
;
CNTMUL:	ADD	HL,DE		;add 'DE' to 'HL'
	DEC	A		;one less to go
	JP	NZ,CNTMUL
	POP	DE		;restore current 'DE', delay in 'HL'
	RET
;.....
;
;
;=======================================================================
;
; Loads a command line addressed by 'DE' registers (max # characters in
; line in 'DE', number of characters in line in DE+1, line starts in
; DE+2) into FCB addressed by 'HL' registers.  The FCB should be at least
; 33 bytes in length.  The command line buffer must have a maximum length
; at least one more than the greatest number of characters that will be
; needed.
;
CMDLINE:PUSH	AF
	PUSH	BC
	PUSH	DE
	PUSH	HL
	CALL	INITIAL		;fills FCBs with blanks and nulls
	EX	DE,HL		;get start of command line in HL
	INC	HL		;address # bytes in cmd line
	LD	E,(HL)		;load de pair with # bytes
	LD	D,0
	INC	HL
	ADD	HL,DE		;point to byte after last character
	LD	(HL),CR		;  in cmd line and store delimiter
	POP	HL		;restore HL and DE
	POP	DE
	PUSH	DE
	PUSH	HL
	INC	DE		;address start of command
	INC	DE
	CALL	DRIVE
;
NAME1:	LD	C,8		;transfer first filename to FCB
	CALL	TRANS
	CP	CR
	JP	Z,DONEL
	CP	' '		;if space, then start of
	JP	Z,NAME2		;  second filename
	POP	HL		;filetype must be after
	PUSH	HL		;  eighth byte of name
	LD	BC,9
	ADD	HL,BC
	LD	C,3		;transfer type of first file
	CALL	TRANS
	CP	CR
	JP	Z,DONEL
;
NAME2:	LD	A,(DE)		;eat multiple spaces
	CP	' '		;  between names
	JP	NZ,NAME3
	INC	DE
	JP	NAME2
;
NAME3:	POP	HL		;second name starts in 16th byte
	PUSH	HL		;point HL to this byte
	LD	BC,16
	ADD	HL,BC
	CALL	DRIVE
	LD	C,8
	CALL	TRANS
	CP	CR
	JP	Z,DONEL
	POP	HL		;second type starts in 25th byte
	PUSH	HL
	LD	BC,25
	ADD	HL,BC
	LD	C,3
	CALL	TRANS
;
DONEL:	POP	HL
	PUSH	HL
	INC	HL		;point to 1st char of 1st name in FCB
	CALL	SCANL		;check for * (ambiguous names)
	POP	HL
	PUSH	HL
	LD	BC,17		;  to 1st char of second name in FCB
	ADD	HL,BC
	CALL	SCANL
	POP	HL
	POP	DE
	POP	BC
	POP	AF
	RET
;.....
;
;
; Subroutines for CMDLINE section
;
INITIAL:PUSH	HL
	PUSH	BC
	LD	(HL),0
	INC	HL
	LD	B,11
	LD	A,' '
	CALL	INITFIL
	LD	B,5
	XOR	A
	CALL	INITFIL
	LD	B,11
	LD	A,' '
	CALL	INITFIL
	LD	B,4
	XOR	A
	CALL	INITFIL
	POP	BC
	POP	HL
	RET
;.....
;
;
INITFIL:LD	(HL),A
	INC	HL
	DEC	B
	JP	NZ,INITFIL
	RET
;.....
;
;
DRIVE:	INC	DE		;check 2nd byte of filename - if it
	LD	A,(DE)		;  is a ":", then drive was specified
	DEC	DE
	CP	':'
	JP	NZ,DEFDR	;else zero for default drive
	LD	A,(DE)		;('INIT' put zero)
	AND	5FH
	SUB	40H		;calculate drive (A=1, B=2,...)
	LD	(HL),A		;place it in FCB.
	INC	DE		;address first byte
	INC	DE
;
DEFDR:	INC	HL		;name field in FCB
	RET
;.....
;
;
TRANS:	LD	A,(DE)		;transfer from command line to FCB
	INC	DE		;up to number of characters specified
	CP	CR		;in 'C' reg. keep scanning field
	RET	Z		;  without transfer until a delimiting
	CP	'.'		;  field char such as '.', blank, or
	RET	Z		;  CR (for end of commmand line)
	CP	' '
	RET	Z
	DEC	C
	JP	M,TRANS		;once C-reg is less than zero, keep
	LD	(HL),A		;  reading command line but do not
	INC	HL		;  transfer to FCB
	JP	TRANS
;...
;
;
SCANL:	LD	B,8		;scan file name addressed by HL
;
TSTNAM:	LD	A,(HL)
	CP	'*'		;if '*' found, fill in rest of field
	JP	Z,FILL1		;  with '?' for ambiguous name
	INC	HL
	DEC	B
	JP	NZ,TSTNAM
	JP	TSTTYP
;...
;
;
FILL1:	CALL	FILL
;
TSTTYP:	LD	B,3		;scan and fill type field for name
;
TSTTYPL:LD	A,(HL)		;  specified above
	CP	'*'
	JP	Z,FILL2
	INC	HL
	DEC	B
	JP	NZ,TSTTYPL
	RET
;.....
;
;
FILL2:	CALL	FILL
	RET
;.....
;
;
FILL:	LD	(HL),'?'	;routine transfers '?'
	INC	HL
	DEC	B
	JP	NZ,FILL
	RET
;=======================================================================
;
; LISTS DIRECTORY AND GIVES FREE SPACE REMAINING ON THE REQUESTED DRIVE.
;
;
; Disk system reset - currently bypassed, if you wish this feature, put
;	JMP DRLST2 instead of JMP DRLST3 in the eighth line.  The
;	current disk (plus the A: drive) will then reset each DIR re-
;	quest.	You can also reset the disk with the LOG command when
;	when inserting a different one.  This saves a reset each time
;	DIR might be requested.
;
DRLST:	CALL	GETDISK
	ADD	A,'A'		;change to ASCII
	LD	(DRNAME),A	;show for drive name
	LD	(ACTDRV),A	;show for space remaining on drive
;
DRLST1:	JP	DRLST3
;
DRLST2:	LD	C,RESET		;13 reset disk system (RESETDK)
	CALL	BDOS
;
;
; Directory list routine
;
DRLST3:	LD	DE,CMDBUF	;put command line in FCB
	LD	HL,FCB		;  addressed by HL-reg
	CALL	CMDLINE		;  and then
	LD	HL,FCB4
	CALL	INITFCB
	LD	A,(FCB2)	;get drive number
	LD	(FCB4),A
	LD	A,(FCB2+1)
	CP	' '		;if a space (blank) get all names
	PUSH	AF
	CALL	Z,QSTMARK
	POP	AF
	CALL	NZ,MOVNAME	;else move name into FCB
	CALL	DRIVEL
	LD	C,STDMA
	LD	DE,TBUF
	CALL	BDOS
	LD	A,(NOFCOL)	;number of columns into 'A' reg.
	LD	(NAMECT),A	;CRLF after 'NOFCOL' number of columns
	LD	DE,FCB4
	LD	C,SRCHF		;do first search
	CALL	BDOS
	INC	A		;0FFH --> 0 if no file(s) found
	JP	NZ,DIRLOOP
	CALL	ILPRT
	DEFB	'++ FILE NOT FOUND ++',0
	JP	STORAGE		;still show storage on default drive
;
DIRLOOP:CALL	GETADD
	INC	HL		;point to first letter of filename
	LD	DE,PRTNAME
	LD	BC,8
	CALL	MOVER
	INC	DE
	LD	BC,3
	CALL	MOVER
	CALL	ILPRT
;
PRTNAME:DEFB	'        ','.','   ',0	; 8 spaces, period, 3 spaces
;
NEXTSR:	LD	DE,FCB4
	LD	C,SRCHN		;do next search
	CALL	BDOS
	INC	A		;if 0FFH --> 0 then
	JP	Z,STORAGE	;  directory-read finished
	PUSH	AF
	PUSH	DE
	PUSH	HL
	LD	A,(NAMECT)
	DEC	A
	LD	(NAMECT),A	;name count updated
	OR	A
	CALL	Z,CRLF		;terminate line of file names
	JP	NZ,FENCE
	LD	A,(NOFCOL)	;restart columns-per-line count
	LD	(NAMECT),A
	JP	NOFENCE		;fence not needed
;
FENCE:	CALL	ILPRT
	DEFB	' : ',0		;fence if not at end of line or
;				;  LAST FILENAME
NOFENCE:POP	HL
	POP	DE
	POP	AF
	JP	DIRLOOP
;.....
;
;
; Determine storage remaining on default drive
;
STORAGE:CALL	CKCPM3
	LD	C,DSKPAR	;current disk parameter block
	CALL	BDOS
	INC	HL
	INC	HL
	LD	A,(HL)		;get block shift factor
	LD	(BSHIFTF),A
	INC	HL		;bump to block mask
	LD	A,(HL)		;get it
	LD	(BMASK),A
	INC	HL
	INC	HL
	LD	E,(HL)		;get max block number
	INC	HL
	LD	D,(HL)
	EX	DE,HL
	LD	(BMAX),HL	;put it away
	LD	C,DSKALL	;address of cp/m allocation vector
	CALL	BDOS
	EX	DE,HL		;get its length
	LD	HL,(BMAX)
	INC	HL
	LD	BC,0		;initialize block count to zero
;
GSPBYT:	PUSH	DE		;save allocation address
	LD	A,(DE)
	LD	E,8		;set to process 8 blocks
;
GSPLUP:	RLA			;test bit
	JP	C,NOTFRE
	INC	BC
;
NOTFRE:	LD	D,A		;save bits
	DEC	HL
	LD	A,L
	OR	H
	JP	Z,ENDALC	;quit if out of blocks
	LD	A,D		;restore bits
	DEC	E		;count down 8 bits
	JP	NZ,GSPLUP	;do another bit
	POP	DE		;bump to next count
	INC	DE		; of allocation vector
	JP	GSPBYT		;process it
;
ENDALC:	POP	DE		;clear alloc vector pointer from stack
	LD	L,C		;copy block to HL
	LD	H,B
	LD	A,(BSHIFTF)	;get block shift factor
	SUB	3		;convert from records to thousands (k)
	JP	Z,PRTFREE	;skip shifts if 1k blocks
;
FREKLP:	ADD	HL,HL		;multiply blocks by k per block
	DEC	A
	JP	NZ,FREKLP
;
PRTFREE:CALL	DECOUT		;(# of free k bytes now in 'HL')
	LD	DE,FREEMSG
	JP	PRTMSG
;.....
;
;
; Subroutines for 'DRLST' section
;
QSTMARK:LD	A,'?'		;if blank in FCB, put in 11 '?' chars.
	LD	B,11
	LD	HL,FCB4+1
;
QSTLP:	LD	(HL),A
	INC	HL
	DEC	B
	JP	NZ,QSTLP
	RET
;.....
;
;
MOVNAME:LD	HL,FCB2+1
	LD	DE,FCB4+1
	LD	BC,11
	CALL	MOVER
	RET
;.....
;
;
GETADD:	DEC	A		;un-do the INR above
	ADD	A,A		;times 32
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,TBUF		;add buffer offset
	LD	L,A
	LD	H,0
	RET
;.....
;
;
DRIVEL:	LD	A,(FCB4)	;if no drive, use
	OR	A		;  default drive in DRNAME
	JP	Z,PRNTHD
	PUSH	AF
	DEC	A
	LD	E,A
	LD	C,SELDSK
	CALL	BDOS
	POP	AF
	ADD	A,40H		;make 1=A, 2=B, etc., and
	LD	(DRNAME),A	; overwrite default stored below
	LD	(ACTDRV),A
;
PRNTHD:	CALL	ILPRT
	DEFB	'Drive '
;
DRNAME:	DEFB	' :',CR,LF,0
	RET
;.....
;
;
; Initialized storage
;
FREEMSG:DEFB	'k bytes free on drive '
ACTDRV:	DEFB	' :',CR,LF,'$'
;
;
; Uninitialized storage
;
BMAX:	DEFS	2		;highest block number on drive
BMASK:	DEFS	1		;rec/blk - 1
BSHIFTF:DEFS	1		;number of shifts to multiply by rec/blk
;.....
;
;
;=======================================================================
;
; Duplicates 'READ BUFFER' routine same as CP/M function 10, but does
; not use CTL-C (reason for the routine).  Does allow controls U, R, E
; and H (BACKSPACE).  Outputs bell if the input is greater than the
; buffer.
;
INBUF:	PUSH	AF
	PUSH	HL
	PUSH	BC
	PUSH	DE		;'DE' registers must be pushed last
;
INBUFA:	CALL	CLRBUF		;clear the buffer area
	POP	DE		;get address of buffer on retries
	PUSH	DE		;restore stack
	XOR	A
	INC	DE		;address count field
	LD	(DE),A		;initialize with a zero in count byte
	INC	DE
	EX	DE,HL		;address first buffer byte with 'HL'
;
INBUFB:	CALL	KEYIN		;(waits for char)
	CALL	UCASE		;convert to upper case if needed
	CP	CR		;is it <return> (enter command)?
	JP	Z,INBUFR	;if so, then return.
	CP	08H		;CTL-H backspaces over deleted character
	JP	Z,DELETE
	CP	7FH		;is it a delete?
	JP	Z,DELETE
	CP	'U'-40H		;is it a CTL-U?
	JP	Z,INBUFO	;output #, CR, LF, and start over
	CP	'R'-40H		;CTL-R retypes line
	JP	Z,RETYPE
;
INBUFC:	LD	B,A		;save inputted character
	EX	DE,HL		;save 'HL' in 'DE'
	POP	HL		;get address of buffer in 'HL'
	PUSH	HL		;restore stack
	INC	HL		;address count byte
	INC	(HL)		;increase count byte
	DEC	HL		;address maximum
	LD	A,(HL)		;put maximum in 'A'
	INC	HL		;address count
	CP	(HL)		;compare count to maximum
	JP	C,ALERTL	;if maximum, ring bell and wait for cr.
	EX	DE,HL		;restore buffer pointer to 'HL'
	LD	(HL),B		;put inputted character in buffer
	LD	A,B		;output it
	CP	EXITCHR		;exit character?
	JP	Z,INBUFR	;if yes, all done
	CP	20H		;printing character?
	CALL	NC,TYPE		;if yes, print it
	INC	HL		;bump pointer
	JP	INBUFB		;get next character
;...
;
;
DELETE:	EX	DE,HL		;save buffer pointer in 'DE'
	POP	HL		;address beginning of buffer
	PUSH	HL		;restore stack
	INC	HL		;address count field
	LD	A,(HL)
	SUB	1		;decrease count
	LD	(HL),A
	JP	C,NODEL		;don't delete past beginning of buffer
	EX	DE,HL		;restore buffer pointer to 'HL'
	DEC	HL		;point to last byte inputted
	LD	A,(HL)		;get the character being deleted
	LD	(HL),' '	;restore blank
	CP	' '		;see if a non-printing character
	JP	C,INBUFB	;if yes, skip the CRT backup
	LD	A,BKSP
	CALL	TYPE		;true erase if 08H
	LD	A,' '
	CALL	TYPE
	LD	A,BKSP
	CALL	TYPE
	JP	INBUFB
;.....
;
;
MORE:	DEFB	'12345'		;5 bytes extra from DELETE routine fix
;
;
NODEL:	INC	(HL)		;do not leave count negative
	EX	DE,HL		;restore pointer to 'HL'
	LD	A,BELL		;says can go no further
	CALL	TYPE
	JP	INBUFB
;.....
;
;
INBUFO:	LD	A,'#'		;announces the line has been removed
	CALL	TYPE
	CALL	CRLF
	JP	INBUFA
;.....
;
;
RETYPE:	POP	DE
	PUSH	DE
	INC	DE		;point to current number of characters
	LD	A,(DE)
	LD	B,A
	LD	A,'#'
	CALL	TYPE
	CALL	CRLF
	LD	A,B		;test if zero input
	OR	A
	JP	Z,INBUFB
;...
;
;
CTLRLP:	INC	DE
	LD	A,(DE)
	CALL	TYPE
	DEC	B
	JP	NZ,CTLRLP
	JP	INBUFB
;.....
;
;
ALERTL:	LD	A,BELL		;alarm for full buffer
	CALL	TYPE
	DEC	(HL)
	EX	DE,HL
	JP	INBUFB
;.....
;
;
PCRLF:	CALL	CRLF
	JP	INBUFB
;.....
;
;
INBUFR:	CALL	CRLF		;1st new line after a command character
	POP	DE
	POP	BC
	POP	HL
	POP	AF
	RET
;.....
;
;
CLRBUF:	POP	DE		;accounts for call
	POP	HL		;restore the registers
	PUSH	HL
	PUSH	DE
	LD	B,(HL)		;save maximum in 'B'
	INC	HL		;point to first buffer byte
	INC	HL
	LD	A,' '
;
CLEARL:	LD	(HL),A
	INC	HL
	DEC	B
	JP	NZ,CLEARL
	RET
;.....
;
;
;=======================================================================
;
; In-line compare.  Compares string addressed by 'DE' to string after
; call (ends with zero).  Return with carry set means strings not the
; same.  All registers except 'A'-reg are unaffected.
;
INLNCP:	EX	(SP),HL		;point 'HL' to 1st character
	PUSH	DE
;
ILCOMPL:LD	A,(HL)		;'HL' points to in-line string
	OR	A		;end of string if zero
	JP	Z,SAME
	LD	A,(DE)
	CP	(HL)
	JP	NZ,NOTSAME
	INC	HL
	INC	DE
	JP	ILCOMPL
;...
;
;
NOTSAME:XOR	A		;if not same, finish through
;
NSLP:	INC	HL		;  string so return will
	CP	(HL)		;  go to instruction after
	JP	NZ,NSLP		;  string and not remainder of string
	SCF
;
SAME:	POP	DE
	INC	HL		;avoids a NOP instruction
	EX	(SP),HL		;  when returning
	RET
;.....
;
;
;=======================================================================
;		  MULTI-FILE ACCESS ROUTINE
;
; Multi-file access subroutine.  Allows processing of multiple files
; (i.e., *.ASM) from disk.  Builds the correct name in the FCB each time
; it is called.  The command is used in programs to process single or
; multiple files.  The FCB is set up with the next name, ready to do
; normal processing (open, read, etc.) when routine is called.	Carry is
; set if no more names are found.
;
MFNAM:	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,STDMA
	LD	DE,TBUF
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	XOR	A
	LD	(FCBEXT),A
	LD	A,(MFFLG1)
	OR	A
	JP	NZ,MFNAM1
	LD	A,1
	LD	(MFFLG1),A
	LD	HL,FCB
	LD	DE,MFNAM5
	LD	BC,12
	CALL	MOVER
	LD	A,(FCB)
	LD	(MFNAM6),A	;save disk in current FCB
	LD	HL,MFNAM5
	LD	DE,FCB
	LD	BC,12
	CALL	MOVER
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,SRCHF
	LD	DE,FCB
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	JP	MFNAM2
;...
;
;
MFNAM1:	LD	HL,MFNAM6
	LD	DE,FCB
	LD	BC,12
	CALL	MOVER
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,SRCHF
	LD	DE,FCB
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
	LD	HL,MFNAM5
	LD	DE,FCB
	LD	BC,12
	CALL	MOVER
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	C,SRCHN
	LD	DE,FCB
	CALL	BDOS
	POP	HL
	POP	DE
	POP	BC
;
MFNAM2:	INC	A
	SCF
	JP	NZ,MFNAM3
	LD	(MFFLG1),A
	RET
;.....
;
;
MFNAM3:	DEC	A
	AND	3
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,81H
	LD	L,A
	LD	H,0
	PUSH	HL		;save name pointer
	LD	DE,MFNAM6+1
	LD	BC,11
	CALL	MOVER
	POP	HL
	LD	DE,FCB+1
	LD	BC,11
	CALL	MOVER
	XOR	A
	LD	(FCBEXT),A
	LD	(FCBRNO),A
	RET
;.....
;
;
MOVER:	LD	A,2
	INC	A
	JP	PE,MFNAM4
	DEFB	0EDH,0B0H	;Z-80 'LDIR' instruction
	RET
;.....
;
;
MFNAM4:	LD	A,(HL)		;used if an 8080 CPU is active
	LD	(DE),A
	INC	HL
	INC	DE
	DEC	BC
	LD	A,B
	OR	C
	JP	NZ,MFNAM4
	RET
;.....
;
;		(END OF MULTI-FILE ACCESS ROUTINE)
;=======================================================================
;		 CALCULATE FILE TRNSFR TIME
;
;
; Shows the time to transfer a file at various baud rates.  (110-19200)
;
SNDTM:	CALL	ILPRT
	DEFB	'File open: ',0
	LD	HL,(RCNT)	;get record count
	CALL	DECOUT		;print decimal number of records
;
	LD	A,(HEXSHO)
	OR	A
	JP	Z,SNDTM1
	CALL	ILPRT
	DEFB	' (',0
	CALL	DHXOUT		;now print size in hex
	CALL	ILPRT
	DEFB	'H)',0
;
SNDTM1:	CALL	ILPRT
	DEFB	' records'
	DEFB	CR,LF,'Send time: ',0
	LD	A,(MSPEED)	;get the speed indicator
	LD	D,0
	LD	E,A		;set up for table access
	LD	HL,BTABLE	;point to baud factor table
	ADD	HL,DE		;index to proper factor
	ADD	HL,DE		;factor in 'DE'
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	LD	HL,(RCNT)	;get # of records
	CALL	DVHLDE		;divide HL by value in DE (records/min)
	PUSH	HL
	LD	L,C
	LD	H,B
	CALL	DECOUT		;print the minutes portion
	CALL	ILPRT
	DEFB	' mins, ',0
	LD	HL,RECDBL	;point to divisors for seconds
	LD	DE,0		;   calculation
	LD	A,(MSPEED)	;get index for baud rate
	LD	E,A
	ADD	HL,DE		;index into table
	LD	A,(HL)		;get multiplier
	POP	HL		;get remainder
	CALL	MULHLA		;multiply the 'HL' x 'A'
	CALL	SHFTHL
	CALL	SHFTHL
	CALL	SHFTHL
	CALL	SHFTHL
	LD	H,0
	CALL	DECOUT		;print the seconds portion
	CALL	ILPRT
	DEFB	' secs at ',0
	CALL	PRTBAUD
	CALL	ILPRTQ
	DEFB	'To cancel: use CTL-X',CR,LF,0
	RET
;
BTABLE:	DEFW	5,13,20,26,29,48,85,152,280,480,0  ;records/min for..
RECDBL:	DEFB	192,74,48,37,33,20,11,6,3,2,0	   ;110-19200 baud
;.....
;
;
; Shows baud rates set for 'time to send' file transfer
;
PRTBAUD:LD	HL,BAUDSPD
	LD	D,0
	LD	A,(MSPEED)	;get baud rate code
	LD	E,A		;x1
	ADD	A,A		;x2
	ADD	A,A		;x4
	ADD	A,E		;x5
	ADD	A,E
	LD	E,A
	ADD	HL,DE		;point to correct rate
	EX	DE,HL
	LD	C,PRINT
	CALL	BDOS
	CALL	ILPRT
	DEFB	' bps ',CR,LF,0
	RET
;.....
;
;
BAUDSPD:DEFB	'110$',0,0,'300$',0,0,'450$',0,0,'600$',0,0,'710$',0,0
	DEFB	'1200$',0,'2400$',0,'4800$',0,'9600$',0,'19200$'
;.....
;
;
;---->	DVHLDE: Divides 'HL' by value in 'DE',
;	Upon exit: 'BC'=quotient,'L'=remainder
;
DVHLDE:	PUSH	DE		;save divisor
	LD	A,E
	CPL			;negate divisor
	LD	E,A
	LD	A,D
	CPL
	LD	D,A
	INC	DE		;'DE' is now two's complemented
	LD	BC,0		;init quotient
;
DIVL1:	ADD	HL,DE		;subtract divisor from dividend
	INC	BC		;bump quotient
	JP	C,DIVL1		;loop till sign changes
	DEC	BC		;adjust quotient
	POP	DE		;retrieve divisor
	ADD	HL,DE		;adjust remainder
	RET
;.....
;
;
;---->	MULHLA:  Multiply the value in 'HL' by the value in 'A'
;		 Return with answer in 'HL'
;
MULHLA:	EX	DE,HL		;multiplicand to 'DE'
	LD	HL,0		;init product
	INC	A		;adjust multiplier for zero test
;
MULLP:	DEC	A
	RET	Z
	ADD	HL,DE
	JP	MULLP
;.....
;
;
; Shift 'HL' register pair one bit to the right
;
SHFTHL:	LD	A,L
	RRA
	LD	L,A
	OR	A		;clear the carry
	LD	A,H
	RRA
	LD	H,A
	RET	NC
	LD	A,128
	OR	L
	LD	L,A
	RET
;.....
;
;
;	     (END OF FILE TRNSFR TIME ROUTINE)
;=======================================================================
;			 CRC SUBROUTINES
;
;
; Check 'CRC' bytes of record just received
;
CRCCHK:	PUSH	HL
	LD	HL,(CRCVAL)
	LD	A,H
	OR	L
	POP	HL
	RET	Z
	LD	A,0FFH
	RET
;.....
;
;
; Generate the CRC tables for fast calculations
;
CRCGN:	LD	HL,CRCTBL	;address at start of 'CRC' lookup table
	LD	C,0
;
CRCGN1:	EX	DE,HL		;store table location into 'DE'
	LD	HL,0		;clear 'HL' pair
	LD	A,C
	PUSH	BC
	LD	B,8
	XOR	H
	LD	H,A
;
CRCGN2:	ADD	HL,HL		;index into the table
	JP	NC,CRCGN3
	LD	A,16		;using x^ 16 + x^12 + x^5 + 1 algorithm
	XOR	H		;(called 'SDLC' networking algorithm)
	LD	H,A
	LD	A,32+1
	XOR	L
	LD	L,A
;
CRCGN3:	DEC	B
	JP	NZ,CRCGN2	;make 8 loops, one for each bit
;
;
; Value now in 'HL', table address still stored in 'DE'.  Exchange, and
; store the 'CRC' value in the two tables after splitting.
;
	POP	BC		;finished borrowing the 'B' register
	EX	DE,HL		;address back in 'HL', 'CRC' in 'DE'
	LD	(HL),D		;store 1st part of 'CRC' value
	INC	H		;move up 256 bytes
	LD	(HL),E		;store 2nd part of 'CRC' value
	DEC	H		;move back 256 bytes
	INC	HL		;increment to next location
	INC	C		;done when 'C' reg. turns zero again
	JP	NZ,CRCGN1	;now go do the next location
	RET
;.....
;
;
; Update the CRC value from a character in the 'A' register
;
CRCUPD:	PUSH	AF		;save all registers just in case
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	HL,(CRCVAL)	;get current value
	EX	DE,HL		;put in 'DE' for now
	LD	B,0
	XOR	D
	LD	C,A		;now have the character in 'BC' pair
	LD	HL,CRCTBL	;start of 'CRC' lookup-table
	ADD	HL,BC		;index into the 'CRC' table
	LD	A,(HL)		;get the value from the table
	XOR	E
	LD	D,A
	INC	H		;move 256 bytes for 2nd table location
	LD	E,(HL)		;put value there into 'E' register
	EX	DE,HL		;put 'DE' into 'HL'
	LD	(CRCVAL),HL	;updated 'CRC' value with this character
	POP	HL		;restore all registers
	POP	DE
	POP	BC
	POP	AF
	RET
;.....
;
;==================== END OF CRC SUBROUTINE ============================
;
;
;=========================START OF MENU ================================
;
;
MENU0:	LD	A,(NFILFLG)
	OR	A
	JP	Z,MENU		;exit if not saving memory for disk file
	CALL	ILPRT		;else print message
	DEFB	CR,LF,'** File still open, use DEL, DIR, WRT, E, L '
	DEFB	'or T ** ',CR,LF,BELL,0
	JP	MENU1
;
MENU:	XOR	A
	LD	(ABTFLG),A	;null the flag
;
MENU1:	LD	HL,RESTSN	;restore record numbers for new file
	LD	DE,RECNOB	;  transfer
	LD	B,RECNOE-RECNOB
	CALL	MOVE
	LD	HL,RSTOPT	;restore option table
	LD	DE,OPTBL
	LD	B,OPTBE-OPTBL
	CALL	MOVE
	XOR	A
	LD	(FSTFLG),A
	LD	(TIMFLG),A
	LD	(FLTRFLG),A	;reset multi-file trans
	LD	(MFFLG1),A
	JP	XPRT
;.....
;
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;		       MENU OF COMMANDS
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
MENU2:	CALL	CLRTST
	CALL	ILPRT
	DEFB	'                  Single Letter Commands',CR,LF,LF
	DEFB	' ?  - Display current settings',CR,LF
MENU3:	DEFB	' ^  - Function key intercept character, '
	DEFB	'then (0-9)',CR,LF
	DEFB	' M  - Display the menu',CR,LF
	DEFB	' E  - Terminal mode with echo',CR,LF
	DEFB	' L  - Terminal mode with local echo',CR,LF
	DEFB	' T  - Terminal mode',CR,LF
	DEFB	'         For copying text to disk use T (E or L) '
	DEFB	'FILENAME.TYP',CR,LF
	DEFB	'         Start or Stop toggles described on subsequent'
	DEFB	' screen.',CR,LF
	DEFB	' R  - Receive CP/M file using Christensen Protocol'
	DEFB	CR,LF
	DEFB	' S  - Send CP/M file using Christensen Protocol',CR,LF
	DEFB	'         COMMAND: R (or S) FILENAME.TYP',CR,LF
	DEFB	'         R and S can use the following subcommands:'
	DEFB	CR,LF
	DEFB	'            B  - Bulk transfer using wildcards '
	DEFB	'(e.g., *.*)',CR,LF
	DEFB	'            D  - Disconnect when done'
	DEFB	CR,LF
	DEFB	'            Q  - Quiet mode (no messages to console)'
	DEFB	CR,LF
	DEFB	'            V  - View <R> or <S> bytes on console'
	DEFB	CR,LF
	DEFB	'            X  - When done, disconnect, go to CP/M'
	DEFB	CR,LF,LF
	DEFB	'      The single letter commands may also be used on '
	DEFB	'the',CR,LF
	DEFB	'      command line when the program is initially '
	DEFB	'executed.',CR,LF,LF,0
;
THRLTR:	CALL	J$NXTSCR
	CALL	ILPRT
	DEFB	'                Three Letter Commands',CR,LF,LF
	DEFB	'CPM - Exit from this program to CP/M',CR,LF
	DEFB	'DIR - List directory and space free (may specify '
	DEFB	'drive)',CR,LF
	DEFB	'ERA - Erase file (may specify drive)',CR,LF
	DEFB	'LOG - Change default drive/user no. (specify '
	DEFB	'drive/user)',CR,LF
	DEFB	'      and reset disks.  e.g. LOG A0: or LOG B:  '
	DEFB	'(user # unchanged)',CR,LF
	DEFB	'SPD - Set file output speed in terminal mode'
	DEFB	CR,LF,0
;
	CALL	SORPTST
	JP	NZ,NOTIME
	CALL	ILPRT
	DEFB	'TIM - Select Baud rate for "time-to-send" msg.',CR,LF,0
;
NOTIME:	LD	A,(TGLECRC)
	OR	A
	JP	Z,NOTOCRC
	CALL	ILPRT
	DEFB	'TCC - Toggle CRC/Checksum mode on receive',CR,LF,0
;
NOTOCRC:LD	A,(TGLELOC)
	OR	A
	JP	Z,NTOGOC
	CALL	ILPRT
	DEFB	'TLC - Toggle local command immediate or after ',0
	LD	A,(EXTCHR)
	CALL	SHFTYPE
	DEFB	CR,LF,0
;
NTOGOC:	LD	A,(TGLELF)
	OR	A
	JP	Z,NTOGUB
	CALL	ILPRT
	DEFB	'TLF - Toggle LF after CR in "L" or "T" mode for '
	DEFB	'a disk file',CR,LF,0
;
NTOGUB:	LD	A,(TGLERUB)
	OR	A
	JP	Z,NTOGF
	CALL	ILPRT
	DEFB	'TRB - Toggle rubout to backspace conversion',CR,LF,0
;
NTOGF:	LD	A,(TGXOFF)
	OR	A
	JP	Z,NTOGOF
	CALL	ILPRT
	DEFB	'TXO - Toggle XOFF testing in terminal mode '
	DEFB	'file output',CR,LF,0
;
NTOGOF:	LD	A,(PMMIMD)	;using a PMMI modem?
	OR	A
	JP	NZ,NONUM
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	NZ,NTOG2
;
NTOG1:	CALL	ILPRT
	DEFB	'NUM - List remote systems',CR,LF,0
;
NTOG2:	LD	A,(STUPTST)
	OR	A
	JP	Z,NONUM
	CALL	ILPRT
	DEFB	'SET - Set modem baud rate',CR,LF,0
;
NONUM:	CALL	ILPRT
	DEFB	'BYE - Disconnect, then return to CP/M'
	DEFB	CR,LF,0
	LD	A,(PMMIMD)	;using a PMMI modem?
	OR	A
	JP	NZ,NONUM0	;if yes, display 'CAL'
	LD	A,(AUTDIAL)	;usina a Hayes-type modem?
	OR	A
	JP	Z,NOPMMI	;exit if neither
;
NONUM0:	CALL	ILPRT
	DEFB	'CAL - Dial number',CR,LF,0
;
NOPMMI:	CALL	ILPRT
	DEFB	'DSC - Disconnect from the phone line',CR,LF,LF
	DEFB	'          The following are terminal text '
	DEFB	'buffer commands:',CR,LF,LF,0
;
SKPLF:	CALL	ILPRT
	DEFB	'DEL - Delete memory buffer and file',CR,LF
	DEFB	'WRT - Write memory buffer to disk file',CR,LF,LF,0
	CALL	NXTSCR
	CALL	ILPRT
	DEFB	'        Local Commands while in Terminal Mode'
	DEFB	CR,LF,LF,0
	LD	A,(BRKCHR)
	CALL	SHFTYPE
	DEFB	'  - Send a break tone for 300 ms.',CR,LF,0
	LD	A,(PMMIMD)
	OR	A
	JP	Z,SKPLF1
	LD	A,(CHGBAUD)
	CALL	SHFTYPE
	DEFB	'  - Change baud rate',CR,LF,0
;
SKPLF1:	LD	A,EXITCHR
	CALL	SHFTYPE
	DEFB	'  - Exit to command mode',CR,LF,0
	LD	A,(TRANLOG)
	OR	A
	JP	Z,NOTLOG
	LD	A,(LOGCHR)
	CALL	SHFTYPE
	DEFB	'  - Send log-on message',CR,LF,0
;
NOTLOG:	LD	A,(NOCONCT)
	CALL	SHFTYPE
	DEFB	'  - Disconnect from the phone line',CR,LF,0
	LD	A,(LSTTST)
	OR	A
	JP	Z,NOLIS
	LD	A,(LSTCHR)
	CALL	SHFTYPE
	DEFB	'  - Toggle printer',CR,LF,0
;
NOLIS:	LD	A,LF
	CALL	TYPE
	LD	A,(SAVECHR)
	CALL	SHFTYPE
	DEFB	'  - Start copy into buffer',CR,LF,0
	LD	A,(UNSAVCH)
	CALL	SHFTYPE
	DEFB	'  - Stop copy into buffer',CR,LF,LF
	DEFB	'             Start & Stop may be toggled as often as '
	DEFB	'desired.',CR,LF
	DEFB	'             A ";" at start of line indicates buffer '
	DEFB	'is copying.',CR,LF
	DEFB	'             XOFF automatically used to stop input '
	DEFB	'when writing',CR,LF
	DEFB	'                full buffer to disk, XON sent to '
	DEFB	'resume.',CR,LF,LF,0
	LD	A,(TRANCHR)
	CALL	SHFTYPE
	DEFB	'  - Transfer ASCII file to remote',CR,LF,LF,0
	LD	A,(LOCNXT)
	OR	A
	LD	A,(EXTCHR)
	JP	NZ,REMDFLT
	CALL	SHFTYPE
	DEFB	'  - Send local control character to remote'
	DEFB	CR,LF,LF,0
	JP	CKSPCL
;
REMDFLT:CALL	SHFTYPE
	DEFB	'  - Next character will be used for local control'
	DEFB	CR,LF,0
;
CKSPCL:	CALL	J$SPMEN		;may have a special menu in the overlay
;				;FALLS ON THROUGH TO 'XPRT'
;
;
;			 (END OF COMMAND MENU)
;=======================================================================
;		    START OF COMMAND LINE HANDLING
;
;
; Check first to see if a file was opened for copying incoming to disk
;
XPRT:	CALL	CRLF		;turn up a blank line to look nice
	LD	A,(NFILFLG)	;have a file open for text mode copy?
	OR	A
	JP	Z,XPRT1		;if not, exit
;
	CALL	GETSPC		;otherwise show remaining space
	CALL	ILPRT
	DEFB	' Bytes of buffer free',CR,LF,LF,0
;
;
; Show disk drive and user number, then command line
;
XPRT1:	LD	C,CURDSK	;current disk function
	CALL	BDOS
	ADD	A,'A'		;make ASCII
	CALL	TYPE
	CALL	GETUSER		;get current user number
	OR	A
	JP	Z,XPRT2		;skip if user 0
	LD	H,0
	LD	L,A
	CALL	DECOUT		;show current user area
;
XPRT2:	LD	A,'>'
	CALL	TYPE
	LD	A,'>'
	CALL	TYPE
	CALL	ILPRT
	DEFB	'COMMAND: ',0
	XOR	A
	LD	(XFLG),A	;null the buffer-length flag
;
;
; Get the command line parameters
;
GTCMD:	LD	DE,CMDBUF	;enter command
	CALL	INBUF
	LD	A,(CMDBUF+2)
	CP	EXITCHR		;exit character
	JP	Z,XPRT1
;
GTCMD1:	CP	'^'		;function key intercept character
	JP	Z,FUNCT		;   (supplied from 'INTCPT' table)
	CP	'?'
	JP	Z,CURPAR
	CP	' '
	JP	Z,XPRT+3	;skip the extra line feed
	LD	A,(CMDBUF+3)
	CP	':'		;see if request for new drive/user
	JP	Z,STDRV
	LD	DE,CMDBUF+2	;point to command
	CALL	INLNCP
	DEFB	'CPM',0
	JP	NC,EXIT
	CALL	CRLF		;(1st CR/LF at 'INBUFR')
	CALL	INLNCP
	DEFB	'LOG',0
	JP	NC,LOGNW
	CALL	INLNCP
	DEFB	'DIR',0
	JP	NC,DIR
	CALL	INLNCP
	DEFB	'ERA',0
	JP	NC,ERASEF
	CALL	INLNCP
	DEFB	'SPD',0
	JP	NC,STSPD
	CALL	INLNCP
	DEFB	'TIM',0
	JP	NC,STTIM
	CALL	INLNCP
	DEFB	'TCC',0
	JP	NC,TGCRC
	CALL	INLNCP
	DEFB	'TRB',0
	JP	NC,TGRUB
	CALL	INLNCP
	DEFB	'TLC',0
	JP	NC,TGLOC
	CALL	INLNCP
	DEFB	'TLF',0
	JP	NC,TGLF
	CALL	INLNCP
	DEFB	'TXO',0
	JP	NC,TGTXOFF
	LD	A,(PMMIMD)	;using a PMMI  modem?
	OR	A
	JP	NZ,NONUM1	;if yes, exit
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	NZ,NONUM1	;if yes, exit
	CALL	INLNCP
	DEFB	'NUM',0
	JP	NC,NUMPR
;
NONUM1:	LD	A,(STUPTST)
	OR	A
	JP	Z,NXOPT1
	CALL	INLNCP
	DEFB	'SET',0
	JP	NC,STUPENT
;
NXOPT1:	CALL	INLNCP
	DEFB	'WRT',0
	JP	NC,WRFIL
	CALL	INLNCP
	DEFB	'DEL',0
	JP	NC,NEWFILE
	CALL	INLNCP
	DEFB	'BYE',0
	JP	NC,BYEBYE
	CALL	INLNCP
	DEFB	'DSC',0
	JP	NC,DONETD
	LD	A,(PMMIMD)	;using a PMMI modem?
	OR	A
	JP	NZ,NXOPT0	;if yes, exit
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,NXOPT2	;exit if neither modem-type
;
NXOPT0:	CALL	INLNCP		;'DE' set from 1st 'INLNCP' call
	DEFB	'CAL',0
	JP	C,NXOPT2
	LD	A,' '		;fool the system
	LD	(CMDBUF+3),A	;  'TBUF' so that it
	JP	DOOPT		;  looks at option for dial
;
NXOPT2:	LD	A,(CMDBUF+2)
	LD	HL,COMLST
	CALL	COMPARE		;compares list pointed to by HL
	JP	C,NOTVLD	;carry set = no match
;
DOOPT:	CALL	STFCB		;loads command buffer into FCB
	CALL	PROCOPT		;check out the options
	JP	RSTRT		;go to work
;.....
;
;
NOTVLD:	CALL	NVLDMS
	JP	XPRT
;.....
;
;
NVLDMS:	CALL	ILPRT
	DEFB	'++ Invalid command ++',CR,LF,BELL,0
	RET
;.....
;
;
FUNCT:	LD	A,(INTCPT)	;get the function key intercept char.
	AND	07FH		;strip off any parity
	PUSH	AF		;save the character for now
	CALL	CLRTST
	CALL	ILPRT
	DEFB	'         SPECIAL FUNCTION KEY TABLE'
	DEFB	CR,LF,LF,0
	POP	AF		;get the character back
	CP	' '		;see if a printing character
	JP	NC,FUNCT1	;if a printing character, show it
	PUSH	AF
	CALL	ILPRT
	DEFB	'CTL-',0
	POP	AF
	ADD	A,40H		;convert binary to ASCII character
;
FUNCT1:	CALL	TYPE		;show on the CRT
	CALL	ILPRT
	DEFB	' current function key intercept character',CR,LF,LF,0
;
;
; Shows the functions of the (0-9) keys
;
	LD	HL,FNCTBL-1	;index into the function key table
	LD	B,10		;has ten entries
;
FUNCT2:	INC	HL		;next table location
	LD	A,(HL)		;get the binary function number
	ADD	A,'0'		;convert binary to ASCII digits
	CALL	TYPE
	LD	A,' '
	CALL	TYPE
;
FUNCT3:	INC	HL		;next table location
	LD	A,(HL)
	OR	A		;see if a binary zero
	JP	Z,FUNCT5
	CP	CR
	JP	NZ,FUNCT4
	CALL	ILPRT
	DEFB	'<CR>',0
	JP	FUNCT3
;
FUNCT4:	CALL	TYPE
	JP	FUNCT3
;
FUNCT5:	CALL	CRLF
	DEC	B
	JP	NZ,FUNCT2
	CALL	CRLF
	JP	XPRT
;.....
;
;
BYEBYE:	LD	A,(PMMIMD)	;using a PMMI modem?
	OR	A
;;;	CALL	NZ,J$GOODBY	;if yes, disconnect
	DB	0,0,0		;(PREVENT DOUBLE DISCONNECT)
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	CALL	NZ,SMRST	;if yes, disconnect
	CALL	J$GOODBY	;user's custom-area goodbye routine
	CALL	ILPRT
	DEFB	CR,LF,'<< Exit to CP/M >>',CR,LF,0
	JP	EXIT		;return to CP/M
;
SMRST:	LD	B,20
	CALL	TIMER
	LD	HL,SM$DISC
	CALL	SNDOUT
	LD	B,20
	CALL	TIMER
	LD	A,' '
;
;
; If showing the +++ and ATH and ATD, etc. move the three semicolons up
; one line.
;
	DEFB	0,0,0
;;;	CALL	TYPE
	LD	HL,SM$ATZ
	CALL	SNDOUT
;
SMRST1:	LD	B,2
	CALL	RECV
	JP	NC,SMRST1
	RET
;.....
;
;
SM$ATZ:	DEFB	'ATZ',CR,'$'
;.....
;
;
DIR:	LD	C,CURDSK
	CALL	BDOS
	LD	(DISKSAV),A
	CALL	DRLST
	LD	A,(DISKSAV)
	LD	E,A
	LD	C,SELDSK
	CALL	BDOS
	JP	XPRT
;.....
;
;
ERASEF:	LD	DE,CMDBUF	;put command line into FCB at 'HL'
	LD	HL,FCB
	CALL	CMDLINE
	CALL	MOVEFCB		;move FCB+16 to FCB
	LD	A,(FCB+1)
	CP	' '
	JP	Z,NOTVLD	;go if no file specified
	LD	DE,FCB
	LD	C,SRCHF
	CALL	BDOS
	INC	A		;0 if file not found
	JP	NZ,ERAFILE	;ok, go erase
	CALL	ILPRT
	DEFB	'++ File not found ++',CR,LF,BELL,0
	JP	XPRT
;.....
;
;
ERAFILE:LD	DE,FCB
	LD	C,ERASE
	CALL	BDOS
	CALL	ILPRT
	DEFB	'File erased',CR,LF,0
	JP	XPRT
;.....
;
;
LOGNW:	LD	A,(NFILFLG)	;file open for memory save to disk?
	OR	A
	JP	NZ,NORESET	;if yes, do not reset disk drive now
	LD	A,(CMDBUF+6)	;any disk drive specified?
	CP	' '
	JP	NZ,LOGNW1	;if not a blank, exit
	CALL	GETDISK		;if not, use current drive
	ADD	A,'A'		;to compensate for next line
;
LOGNW1:	SUB	'A'
	CP	15+1		;for drives 0-15
	JP	NC,NOTVLD	;if more than 15, display error message
	LD	(DISKSAV),A	;store requested drive
	CALL	GETUSER		;pick up current user number
	LD	B,A		;save it
	LD	A,(CMDBUF+7)	;get new user number
	CALL	CHRCK		;check the character
	CALL	FNDUSR
	LD	A,(CMDBUF+8)	;get 2nd digit
	CALL	CHRCK		;check the character
	CALL	FNDUSR+2
;
LOGNW2:	CALL	SVUSER
	LD	C,RESET
	CALL	BDOS
	LD	A,(DISKSAV)
	LD	E,A
	LD	C,SELDSK
	CALL	BDOS
	LD	A,(SAVUSR)
	LD	E,A
	CALL	STUSER
	JP	XPRT
;.....
;
;
CHRCK:	CP	' '
	JP	Z,CHRCK1
	CP	':'		;in case of A: or A1: or A11:  (etc.)
	RET	NZ
;
CHRCK1:	POP	AF		;reset the 'CALL' on the stack
	JP	LOGNW2
;.....
;
;
FNDUSR:	LD	B,0		;zero the 'B' reg. for 1st time through
	CALL	NUMCHK		;if neither, see if a valid number
	LD	C,A		;save
	LD	A,B		;get save first digit
	ADD	A,A		;x2
	ADD	A,A		;x4
	ADD	A,A		;x8
	ADD	A,B		;x9
	ADD	A,B		;x10
	ADD	A,C
	LD	B,A		;save
	RET
;.....
;
;
SVUSER:	LD	A,B
	CP	15+1		;user numbers are 0-15
	JP	NC,NOTVLD
	LD	(SAVUSR),A
	RET
;.....
;
;
NUMGET:	LD	DE,CMDBUF
	CALL	INBUF
	LD	A,(CMDBUF+2)	;get number
	CP	' '
	RET	Z
;
NUMCHK:	SUB	'0'		;remove ascii bias
	CP	9+1
	RET	C		;ok if 9 or less
	POP	HL		;remove 1st call from the stack
	POP	HL		;remove 2nd call from the stack
	JP	NOTVLD
;
GETUSER:LD	E,0FFH		;get current user
;
STUSER:	LD	C,USER		;set up BDOS call
	JP	BDOS
;.....
;
;
GETDISK:LD	C,CURDSK	;get current drive
	JP	BDOS
;.....
;
;
NORESET:CALL	ILPRT
	DEFB	'++      Terminal mode file open      ++',CR,LF
	DEFB	'++ Use WRT or DEL before LOG command ++',CR,LF
	DEFB	CR,LF,BELL,0
	XOR	A
	JP	XPRT
;.....
;
;
STSPD:	CALL	ILPRT
	DEFB	'Delay between chars. (0-9): ',0
;
NOKYS:	CALL	STAT
	JP	Z,NOKYS
	CALL	KEYIN
	CALL	TYPE
	CALL	SAVEA
	SUB	'0'
	CP	10
	JP	NC,NOTVLD
	LD	(BYTDLY),A
;
	CALL	ILPRT
	DEFB	'Delay at end of line (0-9): ',0
;
NOKYS1:	CALL	STAT
	JP	Z,NOKYS1
	CALL	KEYIN
	CALL	TYPE
	CALL	SAVEA
	SUB	'0'
	CP	10
	JP	NC,NOTVLD
	LD	(CRDLY),A
;
SPDMSG:	CALL	ILPRT
	DEFB	CR,LF,'Char. delay (terminal file mode) is:  ',0
	LD	A,(BYTDLY)
	LD	B,A
	LD	A,B
	PUSH	HL
	LD	L,A
	LD	H,0
	CALL	DECOUT
	POP	HL
	CALL	ILPRT
	DEFB	'0 ms. per character',CR,LF
	DEFB	'Line  delay (terminal file mode) is: ',0
	LD	A,(CRDLY)
	LD	B,A
	PUSH	HL
	LD	L,A
	LD	H,0
	CALL	DECOUT
	POP	HL
	CALL	ILPRT
	DEFB	'00 ms. per character',CR,LF,0
	JP	XPRT
;......
;
;
SAVEA:	PUSH	AF
	CALL	ILPRT
	DEFB	CR,LF,0
	POP	AF
	RET
;.....
;
;
STDRV:	LD	A,(CMDBUF+2)	;get the disk drive
	SUB	'A'		;convert to binary value
	CP	15+1		;for drives 0-15
	JP	NC,NOTVLD
	LD	E,A
	LD	C,SELDSK	;select requested drive
	CALL	BDOS
	LD	A,(CMDBUF+5)	;get user number, if any
	CP	' '		;keep current user area?
	JP	Z,XPRT
	SUB	'0'		;convert to binary value
	CP	1		;if a '1', could be units or tens
	JP	NZ,STDRV1	;if not, numbers stop at 15 so exit
	LD	A,(CMDBUF+6)	;check for a 2nd digit
	CP	'0'
	JP	C,STDRV2	;if less, not a valid number, ignore
	SUB	'0'-10		;leave the '10' in as two digits used
;
STDRV1:	CP	15+1		;user areas are 0-15
	JP	NC,NOTVLD
	LD	E,A
	CALL	STUSER
	JP	XPRT		;back to work
;
STDRV2:	LD	A,1
	JP	STDRV1
;.....
;
;
STTIM:	CALL	SORPTST
	JP	NZ,NOTVLD
	CALL	ILPRT
	DEFB	'Use 0-8 to give baud rate for ''S'' mode '
	DEFB	'time-to-send message,',CR,LF
	DEFB	'where 0=110, 1=300, 2=450, 3=600, 4=710, 5=1200, '
	DEFB	'6=2400, ',CR,LF,'7=4800 8=9600 and 9=19200 Baud.'
	DEFB	CR,LF,LF,'Enter value: ',0
	CALL	NUMGET
	CP	9+1		;only looking for 0-9 answers
	JP	NC,NOTVLD
	LD	(MSPEED),A
	CALL	STTIM1
	JP	XPRT
;.....
;
;
STTIM1:	CALL	SORPTST
	JP	NZ,STTIM2
	CALL	ILPRT
	DEFB	'Rate for the S mode time-to-send message is set to ',0
	JP	STTIM3
;...
;
;
STTIM2:	CALL	ILPRT
	DEFB	'Modem speed is ',0
;
STTIM3:	JP	PRTBAUD
;.....
;
;
SORPTST:LD	A,(STUPTST)	;if setup is 'YES' or PMMIMD is
	LD	B,A		;  'YES' or autodial modem is 'YES'
	LD	A,(PMMIMD)	;  return with zero bit not set
	OR	B
	RET	NZ
	LD	A,(AUTDIAL)
	OR	B
	RET
;.....
;
;
TGCRC:	LD	A,(TGLECRC)	;allowing CRC/CHECKSUM toggle?
	OR	A
	JP	Z,NOTVLD	;if not, exit
	LD	A,(CRCDFLT)	;get present value and switch it
	CPL
	LD	(CRCDFLT),A
	CALL	TGCRC1		;show on CRT it has been changed
	JP	XPRT
;.....
;
;
TGCRC1:	CALL	ILPRT
	DEFB	'Mode: ',0
	LD	A,(CRCDFLT)	;see if set for 'CRC' or 'CHECKSUM'
	OR	A
	JP	Z,CHEKMSG
	CALL	ILPRT
	DEFB	'CRC',CR,LF,0
	RET
;.....
;
;
CHEKMSG:CALL	ILPRT
	DEFB	'CHECKSUM',CR,LF,0
	RET
;.....
;
;
TGRUB:	LD	A,(TGLERUB)
	OR	A
	JP	Z,NOTVLD
	LD	A,(CONVRUB)
	CPL
	LD	(CONVRUB),A
	CALL	TGRUB1
	JP	XPRT
;.....
;
;
TGRUB1:	LD	A,(CONVRUB)
	OR	A
	JP	Z,NORUBMS
	CALL	ILPRT
	DEFB	'Rub is backspace',CR,LF,0
	RET
;.....
;
;
NORUBMS:CALL	ILPRT
	DEFB	'Rub is rub',CR,LF,0
	RET
;.....
;
;
TGLOC:	LD	A,(TGLELOC)
	OR	A
	JP	Z,NOTVLD
	LD	A,(LOCNXT)
	CPL
	LD	(LOCNXT),A
	CALL	TGLOC1
	JP	XPRT
;.....
;
;
TGLOC1:	CALL	ILPRT
	DEFB	'Use ',0
	LD	A,(LOCNXT)
	OR	A
	LD	A,(EXTCHR)
	JP	Z,LOCMSG
	CALL	SHFTYPE
	DEFB	' before local command',CR,LF,0
	RET
;...
;
;
LOCMSG:	CALL	SHFTYPE
	DEFB	' to send local command to remote',CR,LF,0
	RET
;.....
;
;
TGLF:	LD	A,(TGLELF)
	OR	A
	JP	Z,NOTVLD
	LD	A,(ADDLFD)
	CPL
	LD	(ADDLFD),A
	CALL	TGLF1
	JP	XPRT
;.....
;
;
TGLF1:	CALL	ILPRT
	DEFB	'LF ',0
	LD	A,(ADDLFD)	;adding LF after CR?
	OR	A
	JP	NZ,LFMSG	;if yes, exit
	CALL	ILPRT
	DEFB	'NOT ',0
;
LFMSG:	CALL	ILPRT
	DEFB	'sent after CR in "L" or "T" for a disk file',CR,LF,0
	RET
;.....
;
;
TGTXOFF:LD	A,(TGXOFF)
	OR	A
	JP	Z,NOTVLD
	CALL	ILPRT
	DEFB	'Use XOFF testing? (Y/N): ',0
	CALL	GETANS
	JP	C,NOCHG3
	LD	(XOFFTST),A
;
NOCHG3:	CALL	XOFFMSG
	CALL	ILPRT
	DEFB	CR,LF,'Use XON waiting after <CR> (Y/N): ',0
	CALL	GETANS
	JP	C,NOCHG4
	LD	(XONWAIT),A
;
NOCHG4:	CALL	XONMS
	LD	A,(XONWAIT)
	OR	A
	JP	Z,XPRT
	CPL
	LD	(XOFFTST),A	;do not allow both
	CALL	ILPRT
	DEFB	'Therefore ',0
	CALL	XOFFMSG
	JP	XPRT
;.....
;
;
GETANS:	LD	DE,CMDBUF
	CALL	INBUF
	LD	A,(CMDBUF+2)	;get answer
	CP	' '
	CCF			;set the carry flag
	RET	Z
	LD	B,A
	CP	'N'
	LD	A,0
	RET	Z
	LD	A,B
	CP	'Y'
	LD	A,1
	RET	Z
	POP	AF		;preserve stack
	JP	NOTVLD
;.....
;
;
XOFFMSG:CALL	ILPRT
	DEFB	'XOFF testing ',0
	LD	A,(XOFFTST)
	OR	A
	JP	NZ,XOTSTON
	CALL	ILPRT
	DEFB	'NOT ',0
;
XOTSTON:CALL	ILPRT
	DEFB	'used',0
;
XONMS1:	CALL	ILPRT
	DEFB	' in terminal mode file output',CR,LF,0
	RET
;.....
;
;
XONMS:	CALL	ILPRT
	DEFB	'XON ',0
	LD	A,(XONWAIT)
	OR	A
	JP	NZ,XONMS2
	CALL	ILPRT
	DEFB	'NOT ',0
;
XONMS2:	CALL	ILPRT
	DEFB	'automatically tested after CR',0
	JP	XONMS1
;...
;
;
STUPENT:LD	A,(STUPTST)
	OR	A
	JP	Z,NOTVLD
	LD	DE,CMDBUF+1
	CALL	J$STUPR
	LD	A,(AUTDIAL)	;using a Hayes-type modem?
	OR	A
	JP	Z,XPRT		;if not, exit, otherwise
	LD	B,'A'		;  send 'AT',CR to autodial modem
	CALL	SNDCHR		;  to insure its baud rate
	LD	B,'T'		;  matches that just selected
	CALL	SNDCHR
	LD	B,CR
	CALL	SNDCHR
	JP	XPRT
;.....
;
;
NEWFILE:LD	A,(NFILFLG)	;file open for disk save?
	OR	A
	JP	Z,NFILOP	;if not, show "no file open" message
	LD	A,(FCB3+1)	;check that file was requested
	CP	' '
	JP	Z,NFILOP	;if no file, do not erase
	LD	DE,FCB3		;otherwise erase the old file
	LD	C,ERASE
	CALL	BDOS
	XOR	A
	LD	(NFILFLG),A	;no file mentioned, reset flags
	LD	(SAVEFLG),A
	LD	HL,FCB3
	CALL	INITFCB
	LD	HL,BUFFER	;reset flags to bottom of ram just
	LD	(HLSAV),HL	; to insure they are there
	JP	XPRT
;.....
;
;
WRFIL:	LD	A,(NFILFLG)	;saving memory for a disk file?
	OR	A
	JP	Z,NFILOP	;not saving a file, don't bother writing
	CALL	WRFIL1		;close the file
	LD	(SAVEFLG),A
	LD	(WRFLG),A
	LD	HL,FCB3
	CALL	INITFCB		;blank out 'FCB' to written file
	LD	HL,BUFFER	;can not be erased
	LD	(HLSAV),HL	;reset to buffer start for next time
	JP	XPRT
;...
;
;
WRFIL1:	LD	A,(FCB3+1)	;check that file was requested
	CP	' '
	RET	Z
	CALL	WRDSK		;write buffer to disk if not empty
;
WRFIL2:	LD	DE,FCB3		;close the file
	LD	C,CLOSE
	CALL	BDOS
	XOR	A
	LD	(NFILFLG),A	;file written, reset flags
	RET
;.....
;
;
NFILOP:	CALL	ILPRT
	DEFB	'++ No File Open ++',CR,LF,BELL,0
	JP	XPRT
;.....
;
;
; THIS ROUTINE DISPLAYS THE PHONE NUMBERS IN THE LIBRARY
;
NUMPR:	PUSH	HL
	CALL	CLRTST
	CALL	ILPRT
	DEFB	'           Library of Phone Numbers of Remote Systems'
	DEFB	0
	LD	C,18		;number of lines to move
	LD	HL,NUMLIB	;address of source memory
	LD	DE,BUFFER	;address of target memory
	CALL	NEWLINE		;start with CRLF
	LD	(DE),A		;+LF
	INC	DE		;and bump it
;
NUMPR1:	INC	HL		;skip PMMI dialing letter
	INC	HL		;and equal sign
	LD	B,LIBLEN-2	;number of bytes to move
	CALL	MOVE		;move to buffer
	CALL	SPACES		;2 entries + 3 spaces = 63 characters
	PUSH	HL		;save source address
	PUSH	DE		;save destination address
	INC	HL		;skip next two characters
	INC	HL
	LD	DE,17*LIBLEN	;get offset of 17 times entry length
	ADD	HL,DE		;add it to the source address
	POP	DE		;restore destination address
	LD	B,LIBLEN-2	;get length of library entry
	CALL	MOVE		;move another entry
	POP	HL		;restore source address
	CALL	NEWLINE		;start next line
	DEC	C		;one less line to print
	JP	NZ,NUMPR1	;if not finished, do another
	LD	A,'$'
	LD	(DE),A
	LD	C,PRINT
	LD	DE,BUFFER	;point to table of numbers to print
	CALL	BDOS
	CALL	CRLF
	CALL	CRLF
	POP	HL
	JP	XPRT		;finished, back to prompt
;.....
;
;
NEWLINE:LD	A,CR		;puts CRLF at memory pointed by 'DE'
	LD	(DE),A		;store it
	LD	A,LF		;line feed
	INC	DE		;bump pointer
	LD	(DE),A		;store lf
	INC	DE		;bump pointer
	RET
;.....
;
;
SPACES:	LD	A,' '		;space
	LD	(DE),A
	INC	DE		;1
	LD	(DE),A
	INC	DE		;2
	LD	(DE),A
	INC	DE		;3
	RET
;.....
;
;
COMPARE:LD	B,(HL)		;compares 'A' register with list
;
COMPLP:	INC	HL		;addressed by HL - first element
	CP	(HL)		;of list must be number of elements
	JP	Z,VALID		;being compared. returns with
	DEC	B		;carry set if 'A' reg. does not
	JP	NZ,COMPLP	;contain an element in list
	SCF
;
VALID:	RET
;.....
;
;
NXTSCR:	CALL	ILPRT
	DEFB	'HIT any KEY to CONTINUE',0
;
NOKEY1:	CALL	STAT		;get keyboard status
	JP	Z,NOKEY1	;keep looping until keypress
	CALL	KEYIN		;gobble up keypress
	CP	'C'-40H		;control-c to abort?
	JP	NZ,CLRTST
	POP	HL		;clear stack of return address
	CALL	CRLF		;turn up a blank line
	JP	XPRT
;.....
;
;
CLRTST:	LD	A,(SCRNTST)
	OR	A
	JP	NZ,CLRSCR
;.....
;
;
LOTSALF:LD	A,CR
	CALL	TYPE
	LD	B,12
	LD	A,LF
;
LFLOOP:	CALL	TYPE
	DEC	B
	JP	NZ,LFLOOP
	RET
;.....
;
;
CURPAR:	CALL	CLRTST
	CALL	ILPRT
	DEFB	'                Current Settings',CR,LF,LF,0
	CALL	TGCRC1
	CALL	TGRUB1
	LD	A,(LSTTST)
	OR	A
	JP	Z,NOLIS1
	CALL	LSTMS
;
NOLIS1:	CALL	STTIM1
	CALL	ILPRT
	DEFB	'Terminal mode file buffer is ',0
	LD	A,(NFILFLG)	;saving memory for a disk file?
	OR	A
	JP	NZ,ACTIVE	;if yes, go say "active"
	CALL	ILPRT
	DEFB	'in',0		;if not, say "inactive"
;
ACTIVE:	CALL	ILPRT
	DEFB	'active',CR,LF,'Unused portion of buffer is ',0
	CALL	GETSPC
	CALL	ILPRT
	DEFB	' bytes',CR,LF,0
	CALL	TGLOC1
	CALL	TGLF1
	CALL	XOFFMSG
	CALL	XONMS
	CALL	SPDMSG
	CALL	CRLF
	CALL	CRLF
	CALL	CRLF
	JP	XPRT
;.....
;
;
GETSPC:	LD	DE,BUFTOP	;top of memory buffer
	LD	HL,(HLSAV)	;current buffer location
	EX	DE,HL
	XOR	A		;clear the carry bit, if set
	SUB	E
	LD	L,A
	LD	A,H
	SBC	A,D
	LD	H,A
	CALL	DECOUT		;print the space remaining
	RET
;.....
;
;
;***********************************************************************
;
;	D - A - T - A	 A - R - E - A
;
;***********************************************************************
;
;
COMLST:	DEFB	6,'S','R','T','E','L','M'
;.....
;
;
; OPTION TABLE
;
OPTBL	 EQU	$
ANSWFLG: DEFB	'A'
BCHFLG:	 DEFB	'B'
DISCFLG: DEFB	'D'
JMPCMD:	 DEFB	'J'
LOCLFG:	 DEFB	'L'
ORIGFLG: DEFB	'O'
QFLG:	 DEFB	'Q'
RSEEFLG: DEFB	'R'
SSEEFLG: DEFB	'S'
VSEEFLG: DEFB	'V'
XITFLG:	 DEFB	'X'
EPRITY:	 DEFB	'0'		;even parity sub-option (only in S or R mode)
OPRITY:	 DEFB	'1'		;odd parity sub-option	(only in S or R mode)
OPTBE	 EQU	$		;transfer when program initially called.
;
;
; The following must be in the same order as the table above
;
RSTOPT:	DEFB	'A','B','D','J','L','O','Q','R','S','V','X','0','1'
;
;
; The next 14 bytes equal the number of bytes between RECNOB and
; RECNOE.
;
RESTSN:	 DEFB	0,0,0,0,0,0
	 DEFW	BUFFER
	 DEFB	0,0,0,0,0,NAK
;
RECNOB	 EQU	$		;start of table marker
RCVRNO:	 DEFB	0		;\
RECNO:	 DEFB	0,0		; \
ERRCT:	 DEFB	0		;  \
ERRCDE:	 DEFB	0		;   \
EOFLG: 	 DEFB	0		;    \	14 bytes between table markers
RECPTR:	 DEFW	BUFFER		;    /
RECINBF: DEFB	0		;   /
MAXEXT:	 DEFB	0		;  /
RCNT:	 DEFB	0,0		; /
DATAFLG: DEFB	0		;/
BENHERE: DEFB	NAK		;
RECNOE	 EQU	$		;end of table marker
;
;
; Additional 16-bit initialized storage
;
CRCVAL:	 DEFW	0
DIALCT:	 DEFW	0
HLSAV:	 DEFW	BUFFER
HLSAV1:	 DEFW	PBUFF
HLSAV2:	 DEFW	PBUFF
;
;
; Additional general purpose initialized storage
;
ABTFLG:	 DEFB	0
ACKFLG:	 DEFB	0
CRCFLAG: DEFB	0
CRFLAG:	 DEFB	0
CURRENT: DEFB	52		;PMMI 300 baud speed value
DLYFLG:	 DEFB	0		;  (defafaults to 300)
ECHOFLG: DEFB	0
EXACFLG: DEFB	0
FIRSTME: DEFB	0
FNKFLG:	 DEFB	0		;function key activity flag
FSTFLG:	 DEFB	0
LISTFLG: DEFB	0
LOCFLG:	 DEFB	0
MFFLG1:	 DEFB	0
MDCTLB:	 DEFB	07FH
NFILFLG: DEFB	0
ONERR:	 DEFB	0
OPTION:	 DEFB	0
ORIGSAV: DEFB	0
RNGBKFL: DEFB	0
SAVEFLG: DEFB	0
UARTCT:	 DEFB	ORIGMOD		;for originate mode
WRFLG:	 DEFB	0
XFLG:	 DEFB	0
CMDBUF:	 DEFB	80H,0		;command buffer control area
;
;
; General purpose unitialized storage area
;
	 DEFS	128		;storage area for 'CMDBUF'
BGNMS:	 DEFS	2
TIMFLG:	 DEFS	1
FLTRFLG: DEFS	1
CHRFLG:	 DEFS	1
TIMVAL:	 DEFS	2
QUIKTIM: DEFS	2
DISKNO:	 DEFS	1
DISKSAV: DEFS	1
DSTORE:	 DEFS	1
FILECT:	 DEFS	1
FTYCNT:	 DEFS	1
MAXRAM:	 DEFS	1
NAMECT:	 DEFS	1
NBSAVE:	 DEFS	2
OLDUSER: DEFS	1
SNDFLG:	 DEFS	1
SAVUSR:	 DEFS	1
;
FCB3:	 DEFS	33
FCB4:	 DEFS	33
FCBBUF:	 DEFS	15
MFNAM5:	 DEFS	12
MFNAM6:	 DEFS	12		;current name
	 DEFS	100		;minimum stack depth
;
;
EVNPAGE	 EQU	($+255)/256*256	;sets buffers on even page
;
;
	 ORG	EVNPAGE
;
;
STACK	 EQU	EVNPAGE-2	;store original stack pointer
CRCTBL:	 DEFS	512		;two tables of 128 bytes each
BUFFDSK: DEFS	128		;buffer for disk save
BUFFPNT: DEFS	128		;buffer for printer
BUFFER:	 DEFS	1024*BUFSIZ	;send/receive file buffer
BUFTOP:	 DEFS	0		;filled in when length is found
PBUFF	 EQU	$		;printer buffer starts here
NAMEBUF	 EQU	$		;batch-mode filenames buffer
;.....
;
;
; BDOS EQUATES
;
RDCON	EQU	1
WRCON	EQU	2
LIST	EQU	5
PRINT	EQU	9
RDBUF	EQU	10
CONST	EQU	11
CPMVER	EQU	12
RESET	EQU	13
SELDSK	EQU	14
OPEN	EQU	15
CLOSE	EQU	16
SRCHF	EQU	17
SRCHN	EQU	18
ERASE	EQU	19
READ	EQU	20
WRITE	EQU	21
MAKE	EQU	22
REN	EQU	23
CURDSK	EQU	25
STDMA	EQU	26
DSKALL	EQU	27
DSKPAR	EQU	31
USER	EQU	32
FILSIZ	EQU	35
BDOS	EQU	0005H
REIPL	EQU	0
FCB	EQU	5CH
FCBEXT	EQU	FCB+12
FCBSNO	EQU	FCB+32
FCBRNO	EQU	FCB+32
FCB2	EQU	6CH
TBUF	EQU	80H
;.....
;
;
	END
