
VERSION:  EQU	714	;MDM714 (11/11/83)  --	CP/M MODEM PROGRAM
;
;		   COPYRIGHTED 1983 BY IRV HOFF
;
; THIS TELEPHONE MODEM PROGRAM USES THE CHRISTENSEN PROTOCOL.  IT HAS
; BOTH CHECKSUM AND 'CRC' CAPABILITY FOR ERROR-DETECTION. IT SUPPORTS
; DIALING AND AUTO-REDIALING FOR THE S-100 PMMI, HAYES SMARTMODEM 300
; AND 1200 AND U.S. ROBOTICS MODEMS.  IT SUPPORTS UP TO TWO ALTERNATE
; DIALING SYSTEMS SUCH AS 'MCI', 'SPRINT', ETC.  IT IS COPYRIGHTED TO
; DETER COMMERCIALISM.  IT MAY BE USED FOR NON-PROFIT PURPOSES OR CAN
; BE ALTERED OR UPDATED IF AGAIN RELEASED TO THE PUBLIC DOMAIN.
;
; 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.
;
;   In January of 1983, I renamed the then-existing MODEM7 program to
;   MDM7 in order to concentrate on things I thought might be useful
;   and wanted to add -- primarily the ability to readily adapt to most
;   available computers.  (MODEM7 had been written exclusively for the
;   PMMI S-100 plug-in modem.  It did not support computers not using
;   the PMMI modem or not using S-100 plug-in cards.)  Since that time
;   I have attempted to keep the program updated with features suggested
;   from many different users.  If making a new update, please try to
;   upload to TCCBS (313) 846-6127 in the Detroit area so other systems
;   will not only get your version but it will not be ignored by others
;   making additional updates.
;
;   Acknowledgements:  Frank Gaude' has been particularly helpful during
;   the concurrent development of this program and his own COMM7 modem
;   series.  We have in some cases exchanged routines while developing
;   others jointly.  He and Keith Petersen were a source of assistance
;   and inspiration.  My personal thanks to both of them.
;
;	Many others have contributed in the past to make the program
;	what it was when I began this series.  Their achievements range
;	from modest changes to signficant major additions:
;
;	     Ward Christensen, Jim Mills, Mark Zeigler, Keith Petersen,
;	     Paul Kelly, Bruce Ratoff, Ron Fowler, Rich Berg, Bob Clyne,
;	     Bill Earnest, Ben Bronson, Paul Hansknecht, John Mahr, Bob
;	     Plouffe, Sigi Kluger and others.
;
;	I do not mean to slight anyone, and hope I have included those
;	who made the most significant advancements to the program.
;					- Irv 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 100 seconds.
;   (In batch mode it times out in 3 minutes for receive.)  This offers
;   ample opportunity to transfer programs between individuals.
;
;***********************************************************************
;
; 11/11/83  Releasing full source code, including all comments.  Updated
;  MDM714   the telephone library for currently available RCPM systems.
;	    LF not automatically added in "L" half duplex mode now.  Can
;	    be added via "TLF" command if needed.  Renamed the overlays
;	    to allow them to remain independent of further updates.  Ex-
;	    ample:  M7AP-1.ASM, M7GP-1.ASM, M7PC-1.ASM, etc.  Enjoy.
;					- Irv Hoff
;
; 10/10/83  Fixed old PMMI bug - changed line in NXTOPT0 area which now
;  MDM713   reads STA CMDBUF+4 (was +5).  This caused the PMMI modem to
;	    flip to answer mode whenever 'CAL' (auto-dial) function was
;	    used.  This bug had been around for some time and led to
;	    notices of "flakey operation" in earlier MODEM7xx programs.
;	    Did some cleanup.		- Sigi Kluger
;
; 07/27/83  Added two alternate dialing routines such as MCI and SPRINT.
;  MDM712   Fixed "disk space remaining" to read correctly with CP/M+.
;					- Irv Hoff
;
; 07/01/83  Changed TERM to allow use of the printer port for modem use.
;  MDM711				- Irv Hoff
;
; 06/22/83  MAJOR CHANGE:  Added autodialing and automatic continuous
;  MDM710		   redialing for Hayes Smartmodem 300 or 1200,
;			   U.S. Robotics in addition to the PMMI modem
;			   for S-100 computers.
;					- Irv Hoff
;
;  (comments on updates from MDM701 through MDM709 removed to conserve
;  space, they are relatively unimportant with subsequent releases.)
;					- 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)
;
MODCTL1:  EQU	PORT	;modem control port
MODDATP:  EQU	PORT+1	;modem data port
MODRCVB:  EQU	02H	;modem receive bit (DAV)
MODRCVR:  EQU	02H	;modem receive ready
MODSNDB:  EQU	01H	;modem send bit
MODSNDR:  EQU	01H	;modem send ready bit
;
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;		     SPECIAL EQUATES FOR PMMI
;
MODCTL2:  EQU	PORT+3	;modem status port
;
BAUDRP:	  EQU	PORT+2	;modem baud rate port
BRKMSK:	  EQU	0FBH	;mask to set break
EVPARMSK: EQU	20H	;mask to set even parity
NOPARMSK: EQU	10H	;mask to reset to no parity
ODPARMSK: 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 sec.
			;any number 0-255 acceptable
;
;		  (END OF SPECIAL PMMI EQUATES)
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;
BDNMCH:	EQU	75H	;bad name match
BUFSIZ:	EQU	16	;buffer size for file transfer in Kbytes
ERRCRC:	EQU	6	;CRC tries, then switches to CHECKSUM
LIBLEN:	EQU	34	;length of each phone library entry
RUB:	EQU	7FH	;rub
;
YES:	EQU	0FFH
NO:	EQU	0
;
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
;
;
	JMP	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.
;
PMMIMODEM:  DB	YES	;yes=PMMI modem, no=non-PMMI modem
SMARTMODEM: DB	NO	;yes=HAYES Smartmodem, no=non-Hayes
TOUCHPULSE: DB	'T'	;T=touch, P=pulse (Smartmodem-only)
;
CLOCK:	   DB	40	;clock speed x10, up to 25.5 mhz.

			;2 MHz=20, 3.68 MH=37, 4 MHz=40, etc.
MSPEED:	   DB	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:	   DB	5	;default time to send character in
			;terminal mode file transfer (0-9)
			;0=0 delay, 1=10 ms, 5=50 ms, 9=90 ms
CRDLY:	   DB	5	;end-of-line delay after CRLF in terminal
			;mode file transfer for slow BBS systems
			;0=0 delay, 1=100 ms, 5=500 ms, 9=900 ms
NOOFCOL:   DB	5	;number of directory columns
SETUPTST:  DB	NO	;yes=non-PMMI setup routine
SCRNTEST:  DB	NO	;yes=if home cursor and clear screen
			;routine at CLRSCRN
ACKNAK:	   DB	YES	;yes=resend a record after any non-ACK
			;no=resend a record after a valid NAK
BAKUPBYTE: DB	NO	;yes=make .BAK file
CRCDFLT:   DB	YES	;yes=default to CRC checking
			;no=default to Checksum checking
TOGGLECRC: DB	YES	;yes=allow toggling of Checksum to CRC
CONVBKSP:  DB	NO	;yes=convert backspace to rub
TOGGLEBK:  DB	YES	;yes=allow toggling of bksp to rub
ADDLF:	   DB	NO	;no=no LF after CR to send file in
			;terminal mode (added by remote echo)
TOGGLELF:  DB	YES	;yes=allow toggling of LF after CR
TRANLOGON: DB	NO	;yes=allow transmission of logon
			;write logon sequence at location LOGON
SAVCCP:	   DB	YES	;yes=do not overwrite CCP
LOCNXTCHR: DB	NO	;yes=local cmd if EXTCHR precedes
			;no=not local cmd if EXTCHR precedes
TOGGLELOC: DB	YES	;yes=allow toggling of LOCNXTCHR
LSTTST:	   DB	YES	;yes=allow toggling of printer on/off
			;in terminal mode. Set to no if using
			;the printer port for the modem
XOFFTST:   DB	NO	;yes=allow testing of XOFF from remote
			;while sending a file in terminal mode
XONWAIT:   DB	NO	;yes=wait for XON aftersending CR while
			;transmitting a file in terminal mode	
TOGXOFF:   DB	YES	;yes=allow toggling of XOFF testing
IGNORCTL:  DB	YES	;yes=do not send control characters
			;above CTL-M to CRT in terminal mode
			;no=send any incoming CTL-char to CRT
EXTRA1:	   DB	0	;for future expansion
EXTRA2:	   DB	0	;for future expansion
BRKCHR:	   DB	'@'-40H	;^@ = Send a 300 ms. break tone
NOCONNCT:  DB	'N'-40H	;^N = Disconnect from phone line
LOGCHR:	   DB	'L'-40H	;^L = Send logon
LSTCHR:	   DB	'P'-40H	;^P = Toggle printer
UNSAVECHR: DB	'R'-40H	;^R = Close input text buffer
TRANCHR:   DB	'T'-40H ;^T = Transmit file to remote
SAVECHR:   DB	'Y'-40H	;^Y = Open input text buffer
EXTCHR:	   DB	'^'-40H	;^^ = Send next character
;
;
; Equates used only by PMMI routines grouped together here.
;
PULSERATE: DB	200	;125=20pps dialing, 250=10pps
CHGBAUD:   DB	'B'-40H	;^B = Used with PMMIMODEM in terminal
			; mode to change baud rate on fly
;
;
; Handles in/out ports for data and status
;
IN$MODCTL1:	IN	MODCTL1 ! RET	;in modem control port
		DB	0,0,0,0,0,0,0	;spares if needed for non-PMMI
OUT$MODDATP:	OUT	MODDATP	! RET	;out modem data port
		DB	0,0,0,0,0,0,0	;spares if needed for non=PMMI
IN$MODDATP:	IN	MODDATP ! RET	;in modem data port
		DB	0,0,0,0,0,0,0	;spares if needed for non-PMMI
ANI$MODRCVB:	ANI	MODRCVB ! RET	;bit to test for receive ready
CPI$MODRCVR:	CPI	MODRCVR ! RET	;value of receive bit when ready
ANI$MODSNDB:	ANI	MODSNDB ! RET	;bit to test for send ready
CPI$MODSNDR:	CPI	MODSNDR ! RET	;value of send bit when ready
;
;
;====================== SPECIAL PMMI PORTS =============================
;
IN$BAUDRP:	IN	BAUDRP	 ! RET	;in baudrate port
OUT$BAUDRP:	OUT	BAUDRP	 ! RET	;out baudrate port
OUT$MODCTL1:	OUT	MODCTL1	 ! RET	;out modem control port #1
OUT$MODCTL2:	OUT	MODCTL2  ! RET	;out modem control port #2
;
;================== END OF SPECIAL PMMI PORTS ==========================
;
;
LOGONPTR:	DW	LOGON
JMP$DIAL:	JMP	DIAL
JMP$DISCONNT:	JMP	DISCONNT
JMP$GOODBYE:	JMP	GOODBYE
JMP$INITMOD:	JMP	INITMOD
JMP$NEWBAUD:	JMP	NEWBAUD
JMP$NOPARITY:	JMP	NOPARITY
JMP$PARITY:	JMP	PARITY
JMP$SETUPR:	JMP	SETUPR
JMP$SPCLMENU:	JMP	SPCLMENU
JMP$SYSVER:	JMP	SYSVER
JMP$BREAK	JMP	SENDBRK
;
;
; NEXT SIX LINES SHOULD NOT BE CHANGED BY USER OVERLAY AS THESE GO TO
; SPECIFIC LOCATIONS IN THE MAIN PROGRAM, NOT IN THE OVERLAY.
;
JMP$ILPRT:	JMP	ILPRT
JMP$INBUF:	JMP	INBUF
JMP$INLNCOMP:	JMP	INLNCOMP
JMP$INMODEM:	JMP	INMODEM
JMP$NXTSCRN:	JMP	NXTSCRN
JMP$TIMER:	JMP	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
	DB	ESC,79H,0,0,0
	RET
;.....
;
;
CLRSCRN:CALL	ILPRT
	DB	ESC,3AH,0,0,0
	RET
;.....
;
;
;======================= SIGN-ON MESSAGE ==============================	
;
; Send version number and date
;
SYSVER:	LDA	PMMIMODEM	;using the pmmi s-100 modem?
	ORA	A
	JZ	SYSVER1		;go if not
	CALL	ILPRT
	DB	'Version for PMMI S-100 modem starting at port: ',0
	LDA	IN$MODCTL1+1
	CALL	HEXO		;put in pmmi control port number
	CALL	ILPRT
	DB	'H',CR,LF,0
	RET
;.....
;
;
SYSVER1:CALL	ILPRT		;if not using the pmmi s-100 board
	DB	'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:	DS	59		;up to 59 characters allowed
	DB	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.)
;
;
INITMOD:RET
;
;========== NON-PMMI SETUP (SPEED CHANGE, ETC.) IF ANY ==============
;
; Insert your speed change and/or auto-dialing routines here.  Oover950
; BYTES (INCLUDING ANYTHING ADDED FOR 'INITMOD').  (END YOUR ROUTINE
; WITH A RET.)
;
SETUPR:	RET
;
;
; Not needed if using the PMMI board, as it has its own break routine
;
SENDBRK:RET
;
;
;**************** START OF SPECIAL PMMI ROUTINES **********************
;
;
;=======================================================================
;
;			SETS THE BAUD RATE
;
;=======================================================================
;
;
SETBAUD:LDA	ANSWFLG		;if 'O' or 'A' not requested and
	ORA	A		;   baudrate not specified, returns
	JZ	FIXBAUD		;   with current mode and rate
	LDA	ORIGFLG		;if option requested, a blank returns
	ORA	A		;   with current mode and rate
	RNZ			;no change if neither 'O' or 'A' shown
;
FIXBAUD:CALL	GETBAUD		;calculate pmmi baud rate divisor
	CALL	SETMSPD		;set the file time transfer value
	CALL	OUT$BAUDRP	;set the pmmi board to that baudrate
	CPI	52
	MVI	A,5FH		;dtr (filter for over 300 baud)
	JC	GT300		;yes, greater than
	MVI	A,7FH		;dtr (filter for 300 and less baud)
;
GT300:	CALL	OUT$MODCTL2
	STA	MODCTLB		;save modem control byte
;
OFFHOOK:LXI	H,7500		;throw in some delay
;
OFFDLY: DCR	L
	JNZ	OFFDLY
	DCR	H
	JNZ	OFFDLY
	LDA	UARTCTLB	;uart control byte for 'A' or 'O'
	CALL	OUT$MODCTL1	;now set to answer or originate
	MOV	A,C
	STA	MSPEED		;set the file transfer time value
	XRA	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).
;
GETBAUD:LDA	FCB+9		;get 1st digit of requested baudrate
	CPI	' '		;if a space, return with current speed
	LDA	CURRENT
	RZ
	LDA	FCB+9
	ORA	A		;if a null, return with current speed
	LDA	CURRENT
	RZ
;
	LXI	D,FCB+9		;get the requested speed
	LXI	H,0
;
DECLP:	LDAX	D		;get the ascii digit
	INX	D
	CPI	' '
	JZ	DECLP
	CPI	'0'		;numerals are 0-9
	JC	BADRATE
	CPI	'9'+1
	JNC	BADRATE
	SUI	'0'
	MOV	B,H
	MOV	C,L
	DAD	H
	DAD	H
	DAD	B
	DAD	H
	ADD	L
	MOV	L,A
	JNZ	DIGNC
	INR	H
;
DIGNC:	MOV	A,E
	CPI	FCB+12
	JNZ	DECLP
	MOV	A,H
	CMA
	MOV	D,A
	MOV	A,L
	CMA
	MOV	E,A
	INX	D
	LXI	H,15625		;250000/16
	LXI	B,-1
;
DIVLP:	INX	B
	DAD	D
	JC	DIVLP
	MOV	A,B
	ORA	A
	MOV	A,C
	STA	CURRENT		;can use this the next time by default
	RZ
;
BADRATE:CALL	ERXIT
	DB	'++ INVALID BAUDRATE ++$'
;.....
;
;
;=======================================================================
;		    SETS 'MSPEED' TO BAUD RATE
;
;
SETMSPD:MVI	C,0		;changes pmmi mspeed for 110-710 baud
	CPI	100		;<300 baud
	RNC
	INR	C		;c=1 for 300 baud
	CPI	40		;<450 baud
	RNC
	INR	C		;c=2 for 450 baud
	CPI	30		;<600 baud
	RNC
	INR	C		;c=3 for 600 baud
	CPI	24		;<710 baud
	RNC
	INR	C		;c=4 for 710 baud
	RET
;.....
;
;	
; Change baudrate on-the-fly with CTL-B (while in terminal mode)
;
NEWBAUD:LDA	PMMIMODEM
	ORA	A
	RZ
	CALL	ILPRT
	DB	CR,LF,'Enter new Baudrate: ',0
	LXI	H,FCB+9
	MVI	M,' '		;keep current baud if none included
;
NEWBAUD1:
	CALL	KEYIN		;get the baud rate
	CPI	CR		;carriage ret finishes baud rate entry
	CZ	CRLF		;if a 'CR', baud rate has been entered
	JZ	FIXBAUD		;go change the baud rate
;.....
;
;
NEWBAUD2:
	CPI	'0'		;numerals are 0-9
	JC	NEWBAUD1
	CPI	'9'+1
	JNC	NEWBAUD1	;if not a numeral, ignore, ask again
	MOV	M,A		;store answer starting at fcb+9
	CALL	TYPE		;show the numeral on the crt
	INX	H		;next storage location in fcb
	JMP	NEWBAUD1	;get the next numeral
;.....
;
;
;======================= PARITY ROUTINES ===============================
;
;--->PARITY: Routine to setup PMMI for odd/even parity.
;
PARITY:	LDA	PMMIMODEM	;is modem a pmmi?
	ORA	A		;set flags
	RZ			;no, return
	LDA	OPARITY		;get odd parity request byte
	ORA	A		;set flags
	JNZ	EVENPAR		;if not odd see if it is even
	LDA	UARTCTLB	;get uart/modem control byte
	ANI	ODPARMSK
	JMP	PARITY1
;...
;
;
EVENPAR:LDA	EPARITY 	;get even parity request byte
	ORA	A		;set flags
	RNZ			;if even parity not specified return
	LDA	UARTCTLB	;get uart/modem control byte
	ANI	ODPARMSK	;set for parity
	ORI	EVPARMSK	;now set for even parity
;
PARITY1:STA	UARTCTLB	
	JMP	OUT$MODCTL1	;send to pmmi -
;				;WHEN OUT$MODCTL1 DOES RET IT
;.....				;WILL GO BACK TO CALLING ROUTINE
;
;
NOPARITY:
	LDA	PMMIMODEM
	ORA	A
	RZ
	LDA	UARTCTLB	;get uart/modem control byte
	ORI	NOPARMSK	;reset parity bit on pmmi
	JMP	OUT$MODCTL1
;.....
;
;
;==================== END OF PARITY ROUTINES ===========================
;
;
;=======================================================================
;
;		   HAYES/PMMI DIALING ROUTINES
;
;=======================================================================
;
;
	DS	128		;extra area for future expansion
;	
; Modem control command words
;
BRKMASK: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 number
TMPUL:	EQU	80H		;timer pulses mask bit
TRATE:	EQU	250		;value for 0.1 second
;.....
;
;
; Dialing routine
;
DIAL:	LDA	PMMIMODEM	;using a pmmi modem?
	ORA	A
	JNZ	DIAL0
	LDA	SMARTMODEM	;using a hayes smartmodem?
	ORA	A
	RZ			;return if neither modem
;
DIAL0:	XRA	A
	STA	CRFLAG		;zero the continuous dial flag
	STA	RINGBKFL	;zero the ringback flag
	LXI	H,0
	SHLD	DIALCT		;zero the dial count
	LXI	H,CMDBUF+1	;point to the number of characters in..
	MOV	A,M		;..the buffer, then get the number
	CPI	3+1		;anything typed after"cal"?
	JC	DIAL1		;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.
;
	MOV	C,A		;put into the 'C' reg.
	MVI	B,0		;will move original number down 4
	SUI	4		;eliminate the"cal " portion
	MOV	M,A		;store new count at cmdbuf+1
	INX	H		;cmdbuf+2 (first char. of string)
	XCHG			;'DE' now has cmdbuf+2
	LXI	H,CMDBUF+6	;point to number (or letter) to dial
	CALL	MOVER		;move the group down 4 places
	JMP	DIAL4		;check if library number, then dial
;...
;
;
; Comes here if no phone number was manually entered after"CAL" and if
; no phone library letter was entered.	Displays the phone number library
; then asks for an entry.
;
DIAL1:	MVI	C,13		;number of lines to move
	LXI	H,NUMBLIB	;start of phone number library
	LXI	D,BUFFER	;buffer address to store them temporarily
	CALL	NEWLINE		;start with crlf
	STAX	D		;+lf
	INX	D		;and bump it
;
DIAL2:	MVI	B,LIBLEN	;number of bytes to move
	CALL	MOVE		;move to buffer
	CALL	SPACES		;2 entries + 3 spaces = 71 characters
	MVI	B,LIBLEN
	CALL	MOVE
	CALL	NEWLINE
	DCR	C		;one less line to print
	JNZ	DIAL2		;if not zero, print another
;
DIAL3:	MVI	A,'$'		;bdos print routine terminate character
	STAX	D		;store in buffer
	CALL	CLRTST
	MVI	C,PRINT
	LXI	D,BUFFER	;print the library on the crt
	CALL	BDOS
	CALL	ILPRT		;ask which one is wanted
	DB	CR,LF,'Enter library letter or phone number,',CR,LF
	DB	'CTL-X quits while dialing or ringing: ',0
	LXI	D,CMDBUF
	CALL	INBUF
;
;
; You now have either a library letter 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 letter, if so, get the corresponding
; line with phone number from the library.  If a number we ignore the
; library look-up.  (Ringback numbers must end with letter 'R'.)
;
DIAL4:	LXI	H,CMDBUF+1	;number of characters in buffer
	MOV	A,M
	ORA	A		;null means <cr> was typed
	JZ	DIALEXIT	;abort dialing, return to menu
	INX	H		;first typed char of number to dial
	PUSH	H		;save the buufer location
	CALL	DIALBGN		;disconnect, reconnect, wait for tone
	POP	H		;restore the buffer location
	JC	DIALEXIT	;if no dialtone, ponder next move
	MVI	B,'A'		;first letter of alphabet
	MVI	E,0		;counts number of letters to match
	MVI	C,26		;number of letters in alphabet
	MOV	A,M		;get char buffer
;
DIAL5:	CMP	B		;number from table?
	JZ	DIAL6
	INR	B		;make next letter (a-z)
	INR	E		;count up
	DCR	C		;count down
	JZ	DIAL9		;was not a-z so exit
	JMP	DIAL5		;loop
;
;
; Now have a match between the requested letter and one in the library
; E-reg. holds the decimal equivalent (1-26) of the letter (A-Z).
;
DIAL6:	LXI	H,NUMBLIB	;phone number library
	LXI	B,LIBLEN	;length of library entry
	MOV	A,E		;number of times to library length to hl
	ORA	A		;set flags
	JZ	DIAL8
;
DIAL7:	MOV	A,M		;get first char of selected lib entry
	ORA	A		;set flags
	JZ	DIALBAD		;send bad library msg and abort
	DAD	B		;increment 'HL' by library length
	DCR	E		;countdown
	JNZ	DIAL7		;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'
;
DIAL8:	MVI	B,LIBLEN	;number of characters to get from table
	LXI	D,CMDBUF+1	;point to buffer
	XCHG			;'HL' points to cmdbuf+1
	MOV	M,B		;length of each table entry
	XCHG			;restore the registers
	INX	D		;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.
;
DIAL9:	LDA	SMARTMODEM
	ORA	A
	CNZ	SMINIT
;	
DIAL10:	LXI	H,CMDBUF+1
	MOV	E,M		;number of chars in buff
	INX	H		;point to 1st char. to dial
;
DIAL11:	MOV	A,M		;get first number from the buffer
	ORA	A		;set flags
	JZ	DIALBAD		;bad number if a null
;
;
; Dial a digit, check keyboard for abort
;
	CALL	DIALA		;dial a digit, show on crt
	CALL	STAT		;keypress?
	JZ	DIAL13		;if not, exit
	CALL	KEYIN		;yes, go get it
	CPI	CANCEL		;ctl-x?
	JNZ	DIAL13		;if not, exit
	LDA	SMARTMODEM	;using the smartmodem?
	ORA	A
	JZ	DIALEXIT	;if not, exit
;
;
; If using a Smartmodem, backspace 30 times to make sure the entire
; number plus"DT" part of "ATDT" is erased.
;
	MVI	C,30
;
DIAL12: MVI	B,BKSP
	CALL	SENDCHR		;send to the modem to cancel call
	DCR	C
	JNZ	DIAL12		;if not zero, do another
	MVI	B,CR		;terminates try
	CALL	SENDCHR
	MVI	A,' '
	CALL	TYPE		;show on crt
	JMP	DIALEXIT	;now go abort
;
DIAL13:	INX	H		;bump pointer
	DCR	E		;one less character to go
	JNZ	DIAL11		;if not done, loop
;
;
; Show the number of dial attempts
;
	CALL	ILPRT
	DB	' - try #',0
	LHLD	DIALCT		;increment the dial count
	INX	H
	SHLD	DIALCT
	CALL	DECOUT		;show number of attempts so far
	MVI	A,' '		;extra space to position cursor
	CALL	TYPE
;
	LDA	SMARTMODEM
	ORA	A
	JZ	DIAL15		;exit if not using smartmodem
	MVI	B,CR		;tells the smartmodem the number is done
	CALL	SENDCHR		;just have one character to send
;
DIAL14:	CALL	JMP$INMODEM	;catch any output from the smartmodem
	JNC	DIAL14
	JMP	SMRESULT	;finished dialing, get results
;
;
; Dialing is all done
;
DIAL15:	MVI	A,07FH		;turn on 'DTR'
	CALL	OUT$MODCTL2	;timer rate?
	MVI	B,1		;0.1 second per interval
	CALL	TIMER		;wait for modem to turn on 'DTR'
	MVI	A,5DH		;2 stop bits, no parity,..
	CALL	OUT$MODCTL1	;..8 data bits
	MVI	D,4		;clear to send mask
	MVI	C,WAITCTS	;wait time for cts..
	CALL	WAIT		;..(30 seconds, can set 'WAITCTS' for
;				;..UP TO 51 SECONDS FOR EUROPEAN USE)
;
; If connection made, go get options for starting communications
;
	JNC	CONMADE		;connection made
;
;
; Connection not made, see if a redial is desired
;
	CALL	DISCONNT	;hang-up so we can redial if desired
;
DIALAGN:LDA	CRFLAG		;continuous redial flag
	ORA	A
	JNZ	DIALAGN1	;if already set, go dial again
	CALL	ILPRT		;if not, see if should redial again
	DB	CR,LF,LF,'No answer.  Redial? '
	DB	'(Y/N/C): ',BELL,0
	CALL	KBDCHR
	CALL	CRLF		;new line
	CPI	'N'		;redial?
	JZ	MENU		;no, go menu
	CPI	'Y'		;redial?
	JZ	DIALAGN1	;yes, redial
	CPI	'C'		;continuous redial?
	JNZ	DIALAGN		;invalid response, ask again
	MVI	A,1
	STA	CRFLAG		;continuous redial flag
;
DIALAGN1:
	MVI	B,RBWAIT	;5 seconds wait for pmmi reset (or busy)
	CALL	TIMER		;or busy tone may be sensed as dialtone
	CALL	CRLF		;start a new line for new attempt
	LDA	RINGBKFL	;ringback flag set?
	ORA	A
	JZ	DIAL4		;if not, go redial a normal number
	STA	CMDBUF+1	;restore original value including the 'R'
	JMP	DIAL4		;redial entry point
;.....
;
;
; Connection has been made
;
CONMADE:
	LDA	CURRENT		;get current baud rate
	CALL	OUT$BAUDRP	;set baudrate
;
CONMADE1:
	CALL	ILPRT
	DB	CR,LF,LF,'CONNECTED',BELL,0
	LDA	CRFLAG		;continuous redial flag set already?
	ORA	A
	JZ	RETURN		;if not, don'T KEEP RINGING THE BELL
	CALL	ILPRT
	DB	' - hit any key to stop the bell ',0
;
CONMADE2:
	CALL	STAT		;keypress?
	JZ	CONMADE3
	CALL	KEYIN
	XRA	A
	JMP	RETURN		;key pressed, back to terminal mode
;
CONMADE3:
	MVI	B,10
	CALL	TIMER
	MVI	A,BELL
	CALL	TYPE		;ring bell
	JMP	CONMADE2
;.....
;
;
; Automatic dialing routine, prints the number being dialed
;
DIALA:	CALL	TYPE		;print whatever character, dashes, etc.
	CPI	'R'		;could it be a ringback character?
	JNZ	DIALA1		;if not, probably a number so exit
	PUSH	PSW		;save accumulator and flags
	MOV	A,E		;get the character count.  is this"r"..
	CPI	1		;..the last character in the string?
	JZ	RINGBK		;yes, must be ringback char, do ringback
	POP	PSW		;everything back as it was
;
DIALA1:	MOV	B,A		;store the character for now
	CALL	DIALAD		;check for alternate dialing like 'MCI'
	MOV	A,B		;get the original character back
	CPI	','		;smartmodem pause command
	JZ	DIALA2
	CPI	'0'		;digits are 0-9
	RC			;exit less than ascii '0'
	CPI	'9'+1
	RNC			;exit if more than ascii '9'
	SUI	'0'		;strip ascii -- could also do 'ANI 0FH'
	JNZ	DIALA2
	MVI	A,10		;convert zero to 10 pulses
;
;
; Sends the digit to the modem.  If PMMI, converts it to correct number
; of pulses.  Program does not support touch-tone dialing for PMMI.
; Waits 200 ms. after each PMMI digit dialed.
;
DIALA2:	MOV	C,A		;save the character
	LDA	SMARTMODEM	;using a hayes smartmodem?
	ORA	A
	JNZ	SENDCHR		;if smartmodem, exit	
	LDA	PULSERATE	;value for dial speed
	CALL	OUT$BAUDRP
;
DIALA3:	CALL	IN$BAUDRP
	ANI	TMPUL
	JNZ	DIALA3
;
DIALA4:	CALL	IN$BAUDRP
	ANI	TMPUL
	JZ	DIALA4
;
DIALA5:	MVI	A,MAKEM
	CALL	OUT$MODCTL1
;
DIALA6:	CALL	IN$BAUDRP
	ANI	TMPUL
	JNZ	DIALA6
	MVI	A,BRKMASK
	CALL	OUT$MODCTL1
;
DIALA7:	CALL	IN$BAUDRP
	ANI	TMPUL
	JZ	DIALA7
	DCR	C
	JNZ	DIALA5
	MVI	A,MAKEM
	CALL	OUT$MODCTL1
	MVI	B,2		;0.1 second per timer interval
	JMP	TIMER		;return to caller
;.....
;
;
; Print bad library number message and abort if a null is encountered.
;
DIALBAD:
	CALL	ILPRT
	DB	CR,LF,'++ Bad library number called ++',CR,LF,0
;
DIALEXIT:
	CALL	DISCONNT	;abort routine
;
DIALEXT1:
	CALL	CRLF		;turn up a new line
	XRA	A		;reset the continous redial flag
	STA	CRFLAG
;
DIALEXT2:
	CALL	JMP$INMODEM	;catch any output from the modem
	JNC	DIALEXT2
	CALL	JMP$GOODBYE	;user routine to disable 'DTR'
	JMP	MENU
;.....
;
;
; Disconnect from the line, reconnect and wait for the dialtone.
;
DIALBGN:LDA	SMARTMODEM
	ORA	A
	RNZ			;finished if smartmodem
	CALL	DISCONNT
	MVI	A,MAKEM		;make make (off-hook)
	CALL	OUT$MODCTL1	;do it
	MVI	D,DTMSK		;dial tone mask
	MVI	C,50		;waits up to 10 sec. 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.
;
	RNC			;if dial tone within 10 seconds
	CALL	ILPRT		;else, message and return with carry set
	DB	CR,LF,LF,'++ NO DIAL TONE ++',CR,LF,0
	CALL	DISCONNT
	STC			;set the carry bit (no dialtone)
	RET
;.....
;
;
; Do any alternate dialing such as 'MCI' or 'SPRINT'
;
DIALAD:	LDA	SMARTMODEM	;using a smartmodem?
	ORA	A
	RZ			;if not, exit
	LDA	TOUCHPULSE	;using touch tone dialing?
	CPI	'T'
	RNZ			;if not, ignore		
	MOV	A,B		;get the character back
	CPI	'<'		;alternate dialing system #1 (mci?)
	JNZ	DIALAD1		;if not, exit
	PUSH	H		;save the current values
	LXI	H,ALTDIAL1	;alternate dialing area
	JMP	DIALAD2
;
DIALAD1:CPI	'>'		;alternate dialing system #2 (sprint?)
	RNZ			;if neither, exit
	PUSH	H		;save the current values
	LXI	H,ALTDIAL2
;
DIALAD2:MOV	A,M
	CPI	'$'		;ready to terminate?
	JZ	DIALAD3		;if yes, exit
	CALL	TYPE		;show on the crt
	MOV	B,A
	CALL	SENDCHR		;send to the modem
	INX	H		;next location
	CALL	STAT		;keypress?
	JZ	DIALAD2		;if not, do the next character
	CALL	KEYIN		;yes, go get it
	CPI	CANCEL		;ctl-x?
	JZ	DIALEXIT	;if yes, exit
	JMP	DIALAD2		;handle the next character
;
DIALAD3:MVI	A,' '
	CALL	TYPE		;separate from the main number	
	POP	H		;restore the location
	RET	
;.....
;
;
GOODBYE:
DISCONNT:
	LDA	SMARTMODEM	;hayes smartmodem?
	ORA	A
	JNZ	DISCONN1
	XRA	A		;0
	CALL	OUT$MODCTL1	;hang-up
	CALL	OUT$MODCTL2	;clear dav, esd, etc
	PUSH	B
	MVI	B,10		;wait for pmmi to disconnect (1 sec)
	CALL	TIMER		;0.1 second per timer interval
	POP	B
	RET
;...
;	
;
; Disconnect the Smartmodem from the phone line
;
DISCONN1:
	MVI	B,20		;wait 2 seconds
	CALL	TIMER
	LXI	H,SM$DISC
	CALL	SENDOUT
	MVI	B,20		;wait 2 seconds
	CALL	TIMER
	MVI	A,' '
	CALL	TYPE
	LXI	H,SM$DISC1
	CALL	SENDOUT
;
DISCONN2:
	MVI	B,2		;wait 2 seconds
	CALL	RECV
	JNC	DISCONN2
	RET
;...
;
;
SM$DISC:
	DB	'+++$'
;
SM$DISC1:
	DB	'ATH',CR,'$'
;.....
;
;
; Set Smartmodem for pulse dialing
;
SMINIT:	LDA	TOUCHPULSE	;touch or pulse dialing for smartmodem?
	STA	SM$DIAL+3	;store
	LXI	H,SM$DIAL
	CALL	SENDOUT
;
SMINIT1:CALL	JMP$INMODEM
	JNC	SMINIT1		;ignore any input from modem
	RET
;...
;
;
SM$DIAL:DB	'ATDT $'	;set for touch (or pulse) dialing
;.....
;
;
; Send the string pointed to by 'HL' to both the CRT and the modem.
;
SENDOUT:CALL	SENDNOW		;wait until modem is ready
	MOV	A,M		;get the character
	CPI	'$'
	RZ			;if yes, finished
	MOV	A,M
	CALL	OUT$MODDATP	;send to modem
	CALL	TYPE		;show on crt
	INX	H
	JMP	SENDOUT	
;.....	
;
;
; Checks for answer from Smartmodem
;
SMRESULT:
	CALL	RCVREADY	;see if any incoming character yet
	JZ	SMRESUL1	;if yes, exit and look at it
	CALL	STAT		;else see if want to abort ringing
	JZ	SMRESULT	;if neither, wait for one of them
	CALL	KEYIN		;get character from keyboard
	CPI	CANCEL		;ctl-x to terminate dialing?
	JNZ	SMRESULT	;if not, keep going
	MVI	B,CR
	CALL	SENDCHR		;tells the modem to hang up right away
	JMP	DIALEXT1	;abort dialing routine
;
SMRESUL1:
	CALL	IN$MODDATP	;get the character, then
	ANI	7FH		;remove any parity
	MOV	B,A		;store for"givlf" area if needed
	CPI	'C'
	JZ	CONMADE1	;connected, reset redial flags
	CPI	'E'
	JZ	GIVLF		;error, go display
	CPI	'N'
	JNZ	SMRESULT	;none of the above, keep waiting
;
SMDM1:	CALL	INMODEM		;get any more characters from the modem
	JNC	SMDM1		;wait 100 ms. after it is all finished
	JMP	DIALAGN		;go redial
;.....
;
;
; Handles the special ringback numbers.  Dials, lets it ring only once,
; hangs up and then redials.
;
RINGBK:	POP	PSW		;to get it off the stack
	LDA	SMARTMODEM
	ORA	A
	JNZ	RINGBK2
	LDA	CMDBUF+1	;get the number of chars. in the buffer
	STA	RINGBKFL	;store original number including the 'R'
	SUI	1		;subtract 1 to avoid the ringback char
	STA	CMDBUF+1	;store the new value
	MVI	D,DTMSK		;load tone detect mask
	MVI	C,RBLMT		;waits up to 7 seconds for a ring
	CALL	WAIT
	JC	RBTIME		;jump if no ring detected
	MVI	B,25		;got a ring, wait 2.5 sec
	CALL	TIMER
	CALL	IN$BAUDRP	;is tone still present?
	ANA	D
	JZ	DIALAGN		;yes, must be busy
;.....
;
;
; Hang up, redial and listen for dial tone
;
RINGBK1:CALL	HANGUP		;hang up the phone
	MVI	B,RBWAIT	;wait 5 seconds before redialing..
	CALL	TIMER		;..for line to clear, etc.
	CALL	DIALBGN		;disconnect, reconnect, wait for tone
	JNC	DIAL9		;go redial number
	JMP	DIALAGN		;no dial tone heard
;.....
;
;
RINGBK2:CALL	ILPRT
	DB	CR,LF,'++ No ringback for Smartmodem ++',0
	POP	H		;reset the stack
	JMP	DIALEXT1	
;.....
;
;	
RBTIME:	CALL	CRLF
	JMP	RINGBK1		;hangup, redial, & listen for carrier
;.....
;
;
HANGUP: MVI	A,CLEAR
	CALL	OUT$MODCTL2
	XRA	A
	CALL	OUT$MODCTL1	;turn off dtr, originate/answer tones
	RET
;.....
;
;
; 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:	MVI	B,2	
	CALL	TIMER		;wait for timer to go high then low
	CALL	IN$BAUDRP	;pmmiaddr+2 (modem status port)
	ANA	D		;(cts or dialtone mask)
	RZ			;active low, so return on 0
	PUSH	B		;save..
	PUSH	D		;..active reg'S
	CALL	STAT		;keypress?
	JZ	WAIT1		;if not, exit
	CALL	KEYIN		;yes, get char
	CPI	CANCEL		;ctl-x to intentionally abort?
	JZ	WAIT2		;yes, disconnect, jmp to menu
;
WAIT1:	POP	D		;restore..
	POP	B		;..regs
	DCR	C		;count-down
	JNZ	WAIT
	STC			;set carry to indicate mask not set
	RET
;.....
;
;
WAIT2:	POP	D		;reset..
	POP	B		;..stack
	JMP	DONETCD		;disconnect
;.....
;
;	       (END OF HAYES/PMMI DIALING ROUTINE)
;=======================================================================
;			SPECIAL PMMI MENU
;
SPCLMENU:
	LDA	PMMIMODEM
	ORA	A
	RZ
	CALL	JMP$NXTSCRN
	CALL	ILPRT
	DB	'        Additional Subcommands for PMMI Modems'
	DB	CR,LF,LF
	DB	'  Modem control:',CR,LF
	DB	'    A - Answer tone for send or receive',CR,LF
	DB	'    O - Originate tone for send or receive',CR,LF,LF
	DB	'  Parity option:',CR,LF
	DB	'    1 - Set and check for odd parity',CR,LF
	DB	'    0 - Set and check for even parity',CR,LF
	DB	'           Both ends must be capable of these options'
	DB	CR,LF
	DB	'           which are available only in R and S modes.'
	DB	CR,LF
	DB	'           The parity checking will be part of the'
	DB	CR,LF
	DB	'           file transfer protocol.',CR,LF,LF
	DB	'  Speed Options:',CR,LF
	DB	'        After entering your primary and secondary '
	DB	'options,',CR,LF
	DB	'        you can set the modem speed by placing a '
	DB	' "." after',CR,LF
	DB	'        the options followed by the speed e.g., '
	DB	'300, 600.',CR,LF,LF
	DB	'        EXAMPLE:  SBOT.600 will set the modem for '
	DB	'600 baud',CR,LF,0
	DB	RET		;all done

;.....
;			(END OF PMMI MENU)
;=======================================================================
;
;
; Timer routine.  Waits 0.1 seconds for each unit in 'B' reg.
;
TIMER:	PUSH	H
;
TIMER1:	PUSH	B
;
TIMER2:	CALL	JMP$INMODEM	;100 ms. delay per loop
	JNC	TIMER2
	POP	B
	DCR	B
	JNZ	TIMER1
	POP	H		
	RET
;.....
;			
;
;
;************** END OF SPECIAL HAYES/PMMI ROUTINES *********************
;
;
;=======================================================================
;
; CALCULATES DISK SPACE REMAINING IF CP/M+
;
CKCPM3: CALL	CRLF
	MVI	C,CPMVER	;check version #
	CALL	BDOS
	MOV	A,L		
	CPI	30H		;version 3.0?
	RC			;use normal method if not cp/m+
	POP	H		;remove "call ckcpm3" from stack
	MVI	C,CURDSK
	CALL	BDOS
	MOV	E,A
	MVI	C,46		;cp/m+ compute free space call
	CALL	BDOS
	MVI	C,3		;answer is 3 bytes long (24 bits)
;
FREE30: LXI	H,TBUF+2	;answer is located here
	MVI	B,3		;convert to 'K' length
	ORA	A
;
FREE31: MOV	A,M
	RAR
	MOV	M,A
	DCX	H
	DCR	B
	JNZ	FREE31		;loop for 3 bytes
	DCR	C
	JNZ	FREE30		;shift 3 times
	LHLD	TBUF		;get result in 'K'
	JMP	PRTFREE		;display result
;.....
;
;
;=======================================================================
;
;
	DS	15		;allows 'NUMBLIB' to start on even page
;
;
; 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.
;
ALTDIAL1:DB	'xxxxxxx,,,,,,xxxxxxxx,,$'   ;accessed by a < character
;
ALTDIAL2:DB	'xxxxxxx,,,,,,xxxxxxxx,,$'   ;accessed by a > character
;......
;
;
; 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
; sSystem #2.  Either would preceed the acutal number, for example:
;
;	DB    'A=Alan Alda..........<123-456-7890'    ;'A'
;
; -	-     -     -	  -	-     -     -	  -	-     -     -
;
;		'----5---10---15---20---25---30--34'
NUMBLIB:DB	'A=Bob Robesky.......1-209-227-2083'	;'A'
	DB	'B=Byron McKay.......1-415-965-4097'	;'B'
	DB	'C=Dave Crane........1-214-931-8274'	;'C'
	DB	'D=Datatech..........1-415-595-0451'	;'D'
	DB	'E=Bill Earnest......1-215-398-3937'	;'E'
	DB	'F=Chuck Forsberg....1-503-621-3193'	;'F'
	DB	'G=Ron Fowler........1-414-563-9932'	;'G'
	DB	'H=Tampa Bay Bandit..1-813-937-3608'	;'H'
	DB	'I=RBBS Pasadena.....1-213-577-9947'	;'I'
	DB	'J=Walt Jung.........1-301-863-7165'	;'J'
	DB	'K=Sigi Kluger.......1-915-598-1668'	;'K'
	DB	'L=Brian Kantor......1-619-273-4354'	;'L'
	DB	'M=Al Mehr...........1-408-238-9621'	;'M'
	DB	'N=Dick Mead.........1-213-799-1632'	;'N'
	DB	'O=Jud Newell........1-416-231-9538'	;'O'
	DB	'P=Keith Petersen...1-313-759-6569R'	;'P'
	DB	'Q=Mark Pulver.......1-312-789-0499'	;'Q'
	DB	'R=Bruce Ratoff......1-201-272-1874'	;'R'
	DB	'S=Ken Stritzel......1-201-584-9227'	;'S'
	DB	'T=TCBBS, Dearborn...1-313-846-6127'	;'T'
	DB	'U=Paul Traina.......1-408-867-1243'	;'U'
	DB	'V=Thousand Oaks.....1-805-492-5472'	;'V'
	DB	'W=Bill Wood.........1-619-256-3914'	;'W'
	DB	'X=OXGATE............1-408-354-5934'	;'X'
	DB	'Y=Bill Parrott......1-913-682-3328'    ;'Y'
	DB	'Z=George Blat.......1-206-621-8665'	;'Z'
	DB	0					;end
;		'----5---10---15---20---25---30--34'
;.....
;
;
;
;***********************************************************************
;
;
; P - R - O - G - R - A - M    S - T - A - R - T - S	H - E - R - E
;
;
;***********************************************************************
;
;
START:	LXI	H,0
	DAD	SP		;add the current stack pointer to 'HL'
	SHLD	STACK
	LXI	SP,STACK	;start local stack
	CALL	ILPRT
	DB	CR,LF,'MDM',VERSION/100+'0',VERSION MOD 100/10+'0'
	DB	VERSION MOD 10+'0',' - (type M for Menu)',CR,LF,0
	CALL	JMP$SYSVER	;give configuration message
	CALL	CRCGEN		;generate tables for fast 'CRC' check
	CALL	INITADR		;initialize addresses
	CALL	PROCOPT		;process any options
	LDA	OPTION		;any options on the command line?
	CPI	' '+1
	JC	MENU		;if not, show the menu
;
;
; Comes here from menu once the options have been set
;
RESTART:LXI	SP,STACK	;make sure we have a clean stack
	CALL	CKCHAR		;catch any garbage characters left over
	LDA	PMMIMODEM
	ORA	A
	JNZ	RESTART0	;yes, accept 'C' or 'D'
	LDA	SMARTMODEM	;hayes smartmodem?
	ORA	A
	JZ	RESTART1	;if neither, exit
;
RESTART0:
	LDA	OPTION		;get the option
	CPI	'C'		;call (dial) function?
	JZ	JMP$DIAL	;yes, go to it
	LDA	PMMIMODEM
	ORA	A
	CNZ	SETBAUD		;just the pmmi has to check each time
;
RESTART1:
	CALL	MOVEFCB
	XRA	A
	STA	ECHOFLG		;reset echo flag
	STA	LOCFLG		;reset local flag
	LDA	OPTION		;get main option
	CPI	'D'		;disconnect?
	JZ	DONETCD		;yes, disconnect then back to the menu
	CPI	'M'		;menu asked for?
	JZ	MENU2		;go display the menu
	CPI	'R'		;want to receive a file?
	JZ	RCVFIL		;exit if yes
	CPI	'S'		;want to send a file?
	JZ	SENDFIL		;exit if yes
	CPI	'T'		;want terminal mode?
	JZ	DSKSAVE		;exit if yes
	CPI	'E'		;want echo mode?
	JNZ	NOECHO		;if not, exit
	STA	ECHOFLG		;set the echo flag
	JMP	DSKSAVE
;
NOECHO: CPI	'L'		;want local echo mode?
	JNZ	NOLOCAL		;if not, exit
	STA	LOCFLG		;set the local flag
	JMP	DSKSAVE
;	
NOLOCAL:CALL	NTVLDMSG	;say not a valid option
	JMP	MENU		;then go back to the command mode
;.....
;
;
INITADR:LHLD	0000H+1		;bios warm reboot jump vector
	LXI	D,3
	DAD	D
	SHLD	VSTAT+1		;bios console status jump vector
	DAD	D
	SHLD	VKEYIN+1	;bios console keyboard jump vector
	DAD	D
	SHLD	VTYPE+1		;bios console crt jump vector
	DAD	D
	SHLD	VLIST+1		;bios list device jump vector
	LXI	D,30
	DAD	D
	SHLD	GOLIST+1	;bios list device status jump vector
	CALL	GETUSER		;get current user number
	STA	OLDUSER		;save to restore upon exit
	CALL	GETMAX		;find maximum ram for printer use
	JMP	JMP$INITMOD	;initialize non-pmmi systems if needed
;.....
;
;
; Process any options - put 0 in appropriate place in option table if
; option is selected
;
PROCOPT:LXI	D,FCB+1
	LDAX	D
	STA	OPTION
	CPI	' '		;exit if no options
	RZ
;
OPTLP:	INX	D
	LDAX	D
	CPI	' '
	JZ	ENDOPT
	LXI	H,OPTBL
	MVI	B,OPTBE-OPTBL
;
OPTCK:	CMP	M
	JNZ	OPTNO
	CPI	'O'		;want originate tones?
	MOV	B,A		;store momentarily
	MVI	A,ORIGMOD
	JZ	OPTCK1
	MOV	A,B		;get the option back
	CPI	'A'		;want answer tones?
	JNZ	OPTCK2		;if not, exit
	MVI	A,ANSWMOD
;
OPTCK1:	STA	UARTCTLB
;
OPTCK2:	MVI	M,0
	JMP	OPTLP
;
OPTNO:	INX	H
	DCR	B
	JNZ	OPTCK
	CALL	NTVLDMSG
	POP	H		;preserve stack
	JMP	MENU
;
ENDOPT:	LDA	VSEEFLG
	ORA	A
	JNZ	CKOPTN
	STA	QFLG		;quiet mode for watching data items
;
CKOPTN:	LDA	OPTION		;check on the primary option
	CPI	'D'		;going to disconnect?
	RZ
	CPI	'E'		;return if echo option
	RZ
	CPI	'M'		;return if help option
	RZ
	CPI	'L'		;return if local echo option
	RZ
	CPI	'T'
	RZ
	MOV	B,A		;save the primary option for a moment
	LDA	PMMIMODEM	;pmmi modem?
	ORA	A
	JNZ	CKOPTN0		;if yes, accept 'C'
	LDA	SMARTMODEM	;smartmodem?
	ORA	A
	JZ	CKOPTN1		;exit if neither
;
CKOPTN0:MOV	A,B		;get the character back	
	CPI	'C'		;going to call a number now?
	RZ
;
CKOPTN1:LDA	NFILFLG		;saving memory for disk file?
	ORA	A
	JZ	CKOPTN2		;if not, continue
	POP	H		;reset the stack from 'CALL PROCOPT'
	JMP	MENU0		;go show the 'FILE OPEN' message
;
CKOPTN2:MOV	A,B		;get the option back	
	CPI	'S'
	JZ	CKFILE
	CPI	'R'
	JNZ	BDOPT		;none of these, bad option
	LDA	BATCHFLG	;see if the batch mode flag is set
	ORA	A
	RZ			;if yes, exit
;
CKFILE:	LDA	FCB+17		;'S' and 'R' need a file name
	CPI	' '
	RNZ			;exit if a file name is present
;
REENT:	CALL	ILPRT
	DB	'++ Enter primary option plus file name ++'
	DB	CR,LF,BELL,0
	POP	H		;reset stack from 'CALL SETFCB
	JMP	MENU		;abort to command line
;.....
;
;
BDOPT:	CALL	ILPRT
	DB	CR,LF,'++ Bad option ++',CR,LF,LF,0
;.....
;
;
; Check for any garbage characters on line - catch and ignore
;
CKCHAR:	CALL	RCVREADY	;any characters ready to receive?
	RNZ			;if not, return
	CALL	IN$MODDATP	;otherwise get the character and ignore
	JMP	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.
;
DSKSAVE:LDA	BATCHFLG	;batch flag set?
	ORA	A
	JNZ	DSKSAVE1	;if not set, everything is normal
	MVI	A,'B'		;if set, shouldn'T BE, SO RESET IT
	STA	BATCHFLG
	JMP	NOTVLD		;if set, error for"e", "l" or "t"
;
DSKSAVE1:
	LDA	NFILFLG		;already saving for a file?
	ORA	A
	JZ	DSKSAVE2	;exit if not, and open a file
	CALL	BUFMSG		;tell if buffer if on or off
	JMP	TERM
;
DSKSAVE2:
	LDA	FCB+1		;first character of filename (if any)
	CPI	' '		;file specified?
	JNZ	GOODNM		;yes, good name
	XRA	A
	STA	NFILFLG		;show no file being saved
	STA	SAVEFLG		;reset the flag to zero
	JMP	TERM
;...
;
;
GOODNM:	CALL	ERASFIL
	LXI	H,FCB3
	CALL	INITFCB
	LXI	H,FCB		;move the disk name into fcb3 area
	LXI	D,FCB3
	MVI	B,12
	CALL	MOVE
	LXI	D,FCB3		;now make a file from that name
	MVI	C,MAKE
	CALL	BDOS
	LXI	D,FCB3		;now open the file from fcb3
	MVI	C,OPEN
	CALL	BDOS
	LXI	H,BUFFER	;reset pointers to start of buffer
	SHLD	HLSAVE
	MVI	A,1
	STA	NFILFLG		;show now saving to memory for disk file
	CALL	BUFMSG2		;show buffer is available
;
TERM:	LDA	LSTTST		;allowing the printer to be used?
	ORA	A
	CNZ	GOLIST		;if yes, see if anything to print
	CALL	STAT		;keyboard have a character?
	JZ	TERML		;if not, see if any incoming
	CALL	KEYIN		;get character from keyboard
	MOV	B,A		;save for not to protect 'A' reg.
	CPI	BKSP		;test for backspace
	JNZ	NOTBKSP
	LDA	CONVBKSP	;convert backspace to rub?
	ORA	A
	JZ	NOTBKSP		;go if no conversion
	MVI	B,RUB
	JMP	NOTOG
;...
;
;
NOTBKSP:LDA	EXACFLG
	ORA	A		;exact?
	MVI	A,0		;(cannot use 'XRA A' here)
	STA	EXACFLG		;clear for next time
	JZ	NTEXAFLG	;go if exacflg not set 'YES'
	LDA	LOCNXTCHR
	ORA	A		;should we send on exacflg?
	JZ	NOTOG		;jump if loconextchr 'NO'
	LDA	EXTCHR		;we want to send extchr in any case
	CMP	B
	JZ	NOTOG		;send if extchr
	JMP	LOCCHK		;otherwise do local stuff
;...
;
;
NTEXAFLG:
	LDA	EXTCHR		;treat next character in special way?
	CMP	B		;check against this control character
	JNZ	NTEXA1		;yes, set exacflg for next character
	MVI	A,1
	STA	EXACFLG		;set the flag
	JMP	TERM		;do not send, get next character
;
NTEXA1:	LDA	LOCNXTCHR
	ORA	A		;should we send if not exacflg?
	JNZ	NOTOG		;jump if loconextchr 'YES'
;
LOCCHK:	CALL	EXITTST1	;want to exit to menu?
	LDA	NOCONNCT	;want to disconnect from line?
	CMP	B
	JZ	DONETCD		;if yes go disconnect
	LDA	TRANCHR		;output text file to remote?
	CMP	B
	JZ	TRANSFER
	LDA	TRANLOGON
	ORA	A
	JZ	SKPLOGON
	LDA	LOGCHR		;send logon?
	CMP	B
	JZ	SENDLOG
;
SKPLOGON:
	LDA	LSTTST		;going to use the external printer?
	ORA	A
	JZ	NOLST		;if not, skip this area
	LDA	LSTCHR		;get the printer control-character
	CMP	B		;did we just ask for printer control?
	JNZ	NOLST		;if not, exit
	LDA	LISTFLG		;otherwise reset the printer toggle
	CMA
	STA	LISTFLG		;and store
	CALL	CRLF
	CALL	CRLF
	CALL	LSTMSG		;tell if printer is on or off now
	CALL	CRLF
	JMP	TERML		;back to the terminal mode again
;.....
;
;
NOLST:	LDA	BRKCHR		;pmmi break?
	CMP	B
	JZ	BREAK
	LDA	PMMIMODEM	;using a pmmi board?
	ORA	A
	JZ	NOLST1		;if not, skip the next few lines
	LDA	CHGBAUD		;pmmi change baud?
	CMP	B
	PUSH	PSW
	PUSH	H
	CZ	JMP$NEWBAUD
	POP	H
	POP	PSW
	JZ	TERML
;...
;
;
NOLST1:	LDA	UNSAVECHR	;close input buffer?
	CMP	B
	JZ	NOLST2		;if yes, disable copy
	LDA	SAVECHR		;open input buffer?
	CMP	B
	JNZ	NOTOG
	LDA	NFILFLG		;do not allow save if..
	ORA	A
	JZ	TERML		;..this flag is set.
	JMP	NOLST3
;
NOLST2:	XRA	A		;stop copy into file
;
NOLST3:	STA	SAVEFLG
	CALL	BUFMSG
	JMP	TERM		;get next character
;.....
;
;	
;***********************************************************************
;
;			 SEND A CP/M FILE
;
;***********************************************************************
;
;
SENDFIL:XRA	A		;set to checksum initially on send
	STA	CRCFLAG		;..initially on send
	CALL	CKCHAR		;catch any garbage characters
;
SENDFIL1:
	LDA	BATCHFLG	;check if multiple file..
	ORA	A		;..mode is set.
	JNZ	SENDC1
	CALL	ILPRT
	DB	'Ready to send in the batch mode',CR,LF
	DB	'(Use RBT when ready to receive)',CR,LF,0
;
SENDFIL2:
	CALL	JMP$PARITY	;set parity if requested
	MVI	A,1		;indicate send for batch mode
	STA	SENDFLG
	LDA	FSTFLG		;if first time thru..
	ORA	A		;..scan the command line..
	CZ	TNMBUF		;..for multiple names.
	CALL	SENDFN		;sends file name to receiver
	JNC	SENDC2		;if carry is set means no more files.
	MVI	A,'B'		;stop batch..
	STA	BATCHFLG	;..mode option.
	MVI	A,EOT		;final transfer end
	CALL	SEND
	JMP	DONE
;
SENDC1:	LDA	FCB+1
	CPI	' '
	JZ	BLKFILE
;
SENDC2:	CALL	CNREC		;get number of records
	CALL	OPENFIL
	MVI	E,100
	CALL	WAITNAK
;
SENDLP:	CALL	CKABORT		;want to terminate whle sending file?
	CALL	RDRECD
	JC	SENDEOF
	CALL	INCRRNO
	MVI	A,1
	STA	ERRCT
;
SENDRPT:CALL	CKABORT		;want to terminate while sending file?
	CALL	SENDHDR
	CALL	SENDREC
	LDA	CRCFLAG
	ORA	A
	CZ	SENDCKS
	CNZ	SENDCRC
	CALL	GETACK
	JC	SENDRPT
	JMP	SENDLP
;.....
;
;
SENDEOF:MVI	A,EOT
	CALL	SEND
	CALL	GETACK
	JC	SENDEOF
	JMP	DONE
;.....
;
;
;***********************************************************************
;
;		       RECEIVE A CP/M FILE
;
;***********************************************************************
;
;
RCVFIL:	LDA	CRCDFLT		;get mode requested by operator
	STA	CRCFLAG		;store it
	CALL	CKCHAR		;catch any garbage characters
;
RCVFIL1:CALL	JMP$PARITY	;set parity if requested
	LDA	BATCHFLG	;using batch transfer?
	ORA	A
	JNZ	RCVC1		;if not, exit
	XRA	A		;set flag to know where to go
	STA	SENDFLG		;for next file transfer	
	CALL	GETFN		;get the file name
	JNC	RCVC2		;carry set means no more files
	MVI	A,'B'		;done with batch so reset flag
	STA	BATCHFLG
	JMP	DONE
;...
;
;
RCVC1:	LDA	FCB+1		;make sure there is a file named
	CPI	' '
	JNZ	RCVC3		;if yes, exit
	JMP	BLKFILE		;jump if no file named
;...
;
;
RCVC2:	CALL	CKCPM2
	CALL	CKBAKUP
;
RCVC3:	CALL	ERASFIL
	CALL	MAKEFIL
	LDA	BATCHFLG	;using batch transfer?
	ORA	A
	JZ	RCVC4		;if yes, do not print message
	CALL	ILPRT
	DB	'File open - ready to receive',0
;
RCVC4:	CALL	CRLF		;turn up a new line
	LDA	CRCFLAG		;using 'CRC' mode?
	PUSH	PSW		;save the value for now
	ORA	A
	JNZ	RCVC5		;if 'CRC' mode, exit
	CALL	ILPRT		;otherwise using checksum
	DB	'CHECKSUM in effect',CR,LF,0
	JMP	RCVC6
;...
;
;
RCVC5:	CALL	ILPRT
	DB	'CRC in effect',CR,LF,0
	LDA	QFLG		;in quiet mode now?
	ORA	A
	CZ	CRLF		;an extra crlf for quiet mode
;
RCVC6:	POP	PSW		;get the flag'S VALUE BACK, NOW
	ORA	A
	MVI	A,CRC		;in 'CRC' mode?
	JNZ	RCVC7		;if yes, exit
	MVI	A,NAK		;nak for checksum mode
;
RCVC7:	STA	ACKFLG		;now store the 'CRC' (or 'NAK')
;
RCVC8:	MVI	B,1		;catch any characters on input
	CALL	RECV
	JNC	RCVC8		;loop until no more characters
;
RCVLP:	CALL	RCVRECD
	JC	RCVEOT
	CALL	WRRECD
	CALL	INCRRNO
	MVI	A,ACK
	STA	ACKFLG		;display the record count first
	JMP	RCVLP
;.....
;
;
RCVEOT:	CALL	WRBLOCK
	CALL	CLOSFIL
	MVI	A,ACK		;all done copying the file
	CALL	SEND
	JMP	DONE	
;.....
;
;
;=================== FILE TRANSFER IN T-MODE ===========================
;
;
; File transfer routine - called with CTL-T from terminal mode.  Trans-
; fer may be cancelled while sending, by using CTL-X.
;
TRANSFER:
	LXI	H,FCB4
	CALL	INITFCB		;initializes fcbs pointed..
	LXI	H,FCB+16	;..to by 'HL' reg.
	CALL	INITFCB
;
;
; Get name of file to send in"T" (terminal) mode
;
GET:	CALL	ILPRT
	DB	CR,LF,'File name to send?   (RET to quit): ',0
	LXI	D,CMDBUF
	CALL	INBUF
	LDA	CMDBUF+2	;was file entered?
	CPI	' '
	JZ	RETURN		;if not probably wanted to quit
	LXI	D,CMDBUF
	LXI	H,FCB4
	CALL	CMDLINE
	LXI	D,FCB4
	MVI	C,OPEN
	CALL	BDOS
	CPI	0FFH		;return with 0ffh means 'NO SUCH FILE'
	JZ	TRANSL
;
;
; Choice of normal speed or delays between characters / lines
;
	CALL	ILPRT
	DB	'Want to include time delays? (Y/N): ',0
	CALL	KBDCHR
	CPI	'N'		;if 'N' send normal speed
	JZ	DLYSAV
	XRA	A		;otherwise use character/line delays
;
DLYSAV:	STA	DLYFLG		;store the decision
	CALL	CRLF	
	LXI	D,CMDBUF	;make sure cmdbuf has been selected
	MVI	C,SETDMA
	CALL	BDOS
;
;
; Get 128-byte record
;
READMR:	LXI	D,FCB4
	MVI	C,READ
	CALL	BDOS
	ORA	A		;check for a good read
	JZ	READMR1
	DCR	A		;check for end of file to send
	JZ	RETURNS
	CALL	ERXIT		;neither of those, was a read error
	DB	'++ DISK READ ERROR ++','$'
;
;
; Successful read, so send the record
;
READMR1:CALL	SEND80C		;send one 128-char record
	CPI	EOFCHAR		;end of file - omit if object..
	JZ	RETURNS		;..code is to be sent.
	CPI	CANCEL		;cancellation?
	JNZ	READMR
;
RETURN:	CALL	ILPRT
	DB	CR,LF,LF,'(in Terminal-mode now)',CR,LF,LF,0
	CALL	SENDNOW		;insures last character is finished
	CALL	CKCHAR		;catch any echo character on line
	JMP	TERM		;finished, back to t-mode
;.....
;
;
RETURNS:CALL	ILPRT
	DB	CR,LF,'[Transfer completed]',0
	JMP	RETURN
;.....
;
;
TRANSL:	CALL	ILPRT
	DB	CR,LF,BELL,'++ FILE NAME ERROR ++ ',CR,LF,0
	JMP	GET
;.....
;
;
; Send one 128-byes record
;
SEND80C:MVI	B,128		;will send a maximum of 128 character
	LXI	H,CMDBUF	;they are in the cmdbuf area
;
SENDCH1:PUSH	D
	CALL	SPEED		;0-90 ms. delay between characters
	POP	D
	MOV	A,M
	CPI	EOFCHAR
	RZ
	CALL	MODOUT		;send the character to modem
	CALL	STAT		;test to see if
	ORA	A		;cancellation requested
	JZ	SKIP1
	CALL	KEYIN
	CPI	CANCEL
	RZ
;
SKIP1:	INX	H
	DCR	B
	JNZ	SENDCH1
	RET
;.....
;
;
; Send the character to the output
;
MODOUT:	PUSH	PSW		;save the char. so can use 'A' reg.
	CPI	LF
	JNZ	MODOUTL
	LDA	ADDLF		;going to send the line feed to modem?
	ORA	A
	JNZ	MODOUTL		;if yes, exit
	POP	PSW		;get the char. back (a line feed)
	CALL	TYPE		;show on crt, do not send to modem
	RET
;
MODOUTL:LDA	XOFFTST		;waiting for x-off, x-on ?
	ORA	A
	CNZ	TXOFF		;if yes, go check
	CALL	SENDRDY		;wait until modem is ready to send
	JNZ	MODOUTL
	POP	PSW		;get the character back
	CALL	TYPE		;send character to crt
	CALL	OUT$MODDATP	;send character to modem
	CPI	CR		;was it an end of line?
	RNZ			;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.
;
MODOUTN:LDA	XONWAIT		;wait for x-on after cr?
	ORA	A
	JNZ	WAITXON		;if yes, handle separately
	MVI	D,10
;
MODOUTT:PUSH	D
	CALL	SPEED1		;10 ms delay
	POP	D
	DCR	D
	JNZ	MODOUTT		;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:	LDA	BYTDLY		;get delay between characters (0-9)
	JMP	SPEED1+3	;1=10 ms, 5=50 ms, 9=90 ms, etc.
;
SPEED1:	LDA	CRDLY		;get delay after crlf (0-9)
	ORA	A		;100 ms, 5=500 ms, 9=900 ms, etc.
	RZ			;if no delay needed, return
	MOV	C,A		;store number requested in c-reg.
	LDA	DLYFLG		;want any delays this file?
	ORA	A
	RNZ			;if not, skip this section
;
SPEED2:	CALL	SPEED3		;outer loop
	DCR	C
	JNZ	SPEED2
	RET			;done whenever the c-reg. is zero
;...
;
;
SPEED3:	PUSH	H		;save current 'HL' value
	LXI	H,20
	LDA	XOFFTST
	ORA	A
	JZ	SPEED4
	LXI	H,20		;adjust for 'X-OFF' testing
	LDA	ECHOFLG
	ORA	A
	JZ	SPEED4
	LDA	LOCFLG
	ORA	A
	JZ	SPEED4
	LXI	H,25		;adjust for remote echo
;
SPEED4:	CALL	FIXCNT		;multiply delay by clock speed
	XCHG			;transfer delay to 'DE'
	POP	H		;restore current 'HL' from"speed3"
;	
SPEED5:	DCX	D		;inner loop
	LDA	XOFFTST
	ORA	A
	CNZ	TXOFF
	MOV	A,E
	ORA	D
	JNZ	SPEED5
	RET
;...
;
;
TXOFF:	CALL	RCVREADY
	RNZ
	CALL	IN$MODDATP
	ANI	7FH
	CPI	XOFF
	CZ	WAITXON
	RET
;.....
;
;
WAITXON:CALL	RCVREADY	;have a character? (like x-on)
	JNZ	WTXON1		;if no char. see if want to abort
	CALL	IN$MODDATP
	ANI	7FH		;strip off any parity
	CPI	XON		;see if char. was x-on
	RZ			;if yes, keep going
;
WTXON1:	CALL	STAT		;test to see if requesting cancellation
	JZ	WAITXON
	CALL	KEYIN		;can abort if the x-on never comes
	CPI	CANCEL		;ctl-x to abort?
	JNZ	WAITXON		;if not, keep going
	RZ
;.....
;
;================ END OF FILE TRANSFER IN T-MODE =======================
;
;
;***********************************************************************
;
;			   SUBROUTINES	
;
;***********************************************************************
;
SENDFN:	CALL	ILPRTQ
	DB	'Awaiting name NAK',CR,LF,0
	MVI	E,100
	CALL	WAITNLP
	MVI	A,ACK		;got nak, send ack
	CALL	SEND
	LXI	H,FILECT
	DCR	M
	JM	NOMRNM
	LHLD	NBSAVE		;get file name..
	LXI	D,FCB		;..in fcb
	MVI	B,12
	CALL	MOVE
	SHLD	NBSAVE
	CALL	SENDNM		;send it
	ORA	A		;clear carry
	RET
;.....
;
;
NOMRNM:	MVI	A,EOT
	CALL	SEND
	STC
	RET
;.....
;
;
SENDNM:	PUSH	H
;
SENDNM1:MVI	D,11		;count chars in name
	MVI	C,0		;init checksum
	MOV	A,C
	STA	FTYCNT		;initiate file type count
	LXI	H,FCB+1		;address name
;
NAMLPS: MOV	A,M		;send name
	ANI	7FH		;strip high order bit so cp/m 2..
	CALL	SEND		;..won'T SEND R/O FILE DESIGNATION.
	LDA	QFLG
	ORA	A
	MOV	A,M
	CNZ	FTYTST		;type character etc.
;
ACKLP:	PUSH	B		;save cksum
	MVI	B,1		;wait for receiver..
	CALL	RECV		;..to acknowledge..
	POP	B		;..getting letter.
	JC	SCKSER
	CPI	ACK
	JNZ	ACKLP
	INX	H		;next char
	DCR	D
	JNZ	NAMLPS
	MVI	A,EOFCHAR	;tell receiver end of name
	CALL	SEND
	LDA	QFLG
	ORA	A
	CNZ	CRLF
	MOV	D,C		;save checksum
	MVI	B,1
	CALL	RECV		;get checksum..
	CMP	D		;..from receiver.
	JZ	NAMEOK
;
SCKSER:	MVI	A,BDNMCH	;bad name-tell receiver
	CALL	SEND
	CALL	ILPRT
	DB	CR,LF,'++ ERROR sending name  ++',CR,LF,0
	MVI	E,100		;do handshaking over
	CALL	WAITNLP		;don'T PRINT"WAITING READY SIGNAL" MSG
	MVI	A,ACK
	CALL	SEND
	JMP	SENDNM1
;.....
;
;
NAMEOK:	MVI	A,OKNMCH	;good name-tell receiver
	CALL	SEND
	POP	H
	RET	
;.....
;
;
GETFN:	LXI	H,FCB
	CALL	INITFCB+2	;does not initialize drive
	CALL	ILPRT
	DB	'Awaiting FILENAME',CR,LF,0
	CALL	HSNAK
	CALL	GETNM		;get the name
	CPI	EOT		;if eot, then no more files
	JZ	NOMRNMG
	ORA	A		;clear carry
	RET
;.....
;
;
NOMRNMG:STC
	RET
;.....
;
;
GETNM:	PUSH	H
;
GETNM1:	MVI	C,0		;init checksum
	MOV	A,C
	STA	FTYCNT		;initiate count for file type
	LXI	H,FCB+1
;
NAMELPG:MVI	B,5		;wait awhile for file name
	CALL	RECV		;get char
	JNC	GETNM2
	CALL	ILPRT
	DB	CR,LF,'Time out receiving FILENAME',CR,LF,0
	JMP	GCKSER
;.....
;
;
GETNM2:	CPI	EOT		;if eot, then no more files
	JZ	GNRET
	CPI	EOFCHAR		;got end of name
	JZ	ENDNAME
	MOV	M,A		;put name in fcb
	CALL	FTYTST
	PUSH	B		;save cksum
	MVI	A,ACK		;ack getting letters
	CALL	SEND
	POP	B
	INX	H		;get next char
	MOV	A,L		;don'T LET NOISE...
	CPI	7FH		;..cause overflow..
	JZ	GCKSER		;..into program area.
	JMP	NAMELPG
;.....
;
;
FTYTST:	LDA	FTYCNT
	INR	A
	STA	FTYCNT
	CPI	8+1		;are we at the file type?
	JZ	SPCTST		;go if so
;
ENDSPT:	MOV	A,M
	CPI	' '		;test for space
	CNZ	TYPE		;type if not
	RET
;.....
;
;
SPCTST:	MOV	A,M
	CPI	' '		;test for space in first file type byte
	RZ			;don'T OUTPUT PERIOD IF SPACE
	MVI	A,'.'	
	CALL	TYPE
	JMP	ENDSPT		;output first file type byte
;.....
;
;
ENDNAME:LDA	QFLG
	ORA	A
	CNZ	CRLF
	MOV	A,C		;send checksum
	CALL	SEND
	MVI	B,1
	CALL	RECV		;checksum good?
	CPI	OKNMCH		;yes if oknmch sent..
	JZ	GNRET		;..else do over.
;
GCKSER:	LXI	H,FCB		;clear fcb (except drive)..
	CALL	INITFCB+2	;..since it might be damaged..
	CALL	ILPRT
	DB	CR,LF,'++ CHECKSUM error ++',CR,LF,0
	CALL	HSNAK		;do handshaking over
	JMP	GETNM1
;...
;
;
GNRET:	POP	H
	RET
;.....
;
;
HSNAK:	MVI	E,180		;3-minute maximum wait for a file name
;
HSNAK1: CALL	CKABORT		;want to abort?
	MVI	A,NAK		;send nak until receiving ack
	CALL	SEND
	MVI	B,1		;wait up to 1 second for a character
	CALL	RECV
	CPI	ACK		;'ACK' is what we were waiting for
	RZ
	DCR	E		;one less to try
	JNZ	HSNAK1
	JMP	ABORT		;timed out, abort back to command line
;.....
;
;
TNMBUF:	MVI	A,1		;call from sendfil only once.
	STA	FSTFLG
	XRA	A
	STA	FILECT
	CALL	SCAN
	LXI	H,NAMEBUF
	SHLD	NBSAVE		;save addr of 1st name
;
TNLP1:	CALL	TRTOBUF
	LXI	H,FCB
	LXI	D,FCBBUF
	CALL	CMDLINE		;parse name to cp/m format
;
TNLP2:	CALL	MFNAME		;search for names (wildcard format)
	JC	NEXTNM
	LDA	FCB+10		;if cp/m 2 $sys file..
	ANI	80H		;..don'T SEND
	JNZ	TNLP2
	LHLD	NBSAVE		;get name
	LXI	D,FCB		;move it to fcb
	XCHG
	MVI	B,12
	CALL	MOVE
	XCHG
	SHLD	NBSAVE		;addr of next name
	LXI	H,FILECT	;count files found
	INR	M
	JMP	TNLP2
;.....
;
;
NEXTNM:	LXI	H,NAMECT	;count names found
	DCR	M
	JNZ	TNLP1
	LXI	H,NAMEBUF	;save start of buffer
	SHLD	NBSAVE
	LDA	FILECT
	CPI	64+1		;no more than 64 transfers
	RC
	MVI	A,64		;only x'FER FIRST 64
	STA	FILECT
	RET
;.....
;
;
; Tells when buffer is opened/closed for memory save to write on disk
;
BUFMSG:
	CALL	ILPRT
	DB	CR,LF,'** Memory buffer ',0
	LDA	SAVEFLG
	ORA	A
	JZ	BUFMSG1
	CALL	ILPRT
	DB	'open **',CR,LF,LF,';',0
	RET
;
BUFMSG1:
	CALL	ILPRT
	DB	'closed **',CR,LF,LF,0
	RET
;
BUFMSG2:
	CALL	ILPRT
	DB	CR,LF,'** Memory buffer available **',CR,LF,0
	RET
;.....
;
;
; Clear the screen and return to the menu command
;
EXITMENU:
	CALL	CRLF
	CALL	CLREOS		;clear line to clean up any mess
	JMP	MENU0
;.....
;
;
; Checks to see if the modem has a character ready
;
RCVREADY:
	CALL	IN$MODCTL1
	CALL	ANI$MODRCVB
	JMP	CPI$MODRCVR
;.....
;
;
; Checks to see if the modem is ready to receive a character
;
SENDRDY:
	CALL	IN$MODCTL1
	CALL	ANI$MODSNDB
	JMP	CPI$MODSNDR
;.....
;

;
SENDNOW:
	CALL	SENDRDY		;ready to send a character?
	RZ			;exit if ready
	CALL	EXITTEST	;see if want to quit now
	JMP	SENDNOW		;wait some more
;.....
;
;
; Send the log-on message when requested
;
SENDLOG:
	LHLD	LOGONPTR	;'HL' points to start of logon message
	CALL	LOGLP
	JMP	TERML
;...
;
;
LOGLP:	CALL	SENDNOW		;wait until modem is ready
	MOV	A,M		;get logon byte
	ORA	A
	RZ
	CALL	OUT$MODDATP	;otherwise send the character
	MOV	B,A		;save the character so we can use 'A'
	CALL	CKOPTION	;check what option, then display
	INX	H		;next location in message
	JMP	LOGLP		;get next character
;.....
;
;
CKOPTION:
	LDA	OPTION
	CPI	'T'		;in terminal mode?
	JZ	CKOPT1		;if yes, exit
	MOV	A,B		;get the character back
	JMP	TYPE		;display the character, then return
;
CKOPT1:	MVI	B,1
	CALL	RECV		;get the echo character
	RC			;if no character don'T TRY TO PRINT
	JMP	TYPE		;display the character, then return	
;.....
;
;
; Check for exit character
;
EXITTEST:
	CALL	STAT		;anything on keyboard?
	RZ
	CALL	KEYIN		;get it, then
	MOV	B,A		;save to protect the 'A' reg.
;
EXITTST1:
	MVI	A,EXITCHR	;exit character
	CMP	B		;asking to exit to menu?
	RNZ			;if not, back to work
	POP	H		;clear the stack from 'CALL'
	JMP	EXITMENU	;exit to the menu
;.....
;
;
LSTMSG:	CALL	ILPRT
	DB	'Printer buffer is ',0
	LDA	LISTFLG		;see if printer should be on or off
	ORA	A
	JZ	LSTMSG1
	CALL	ILPRT
	DB	'ON',CR,LF,0
	RET
;
LSTMSG1:CALL	ILPRT
	DB	'OFF',CR,LF,0
	RET
;.....
;
;
; 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.
;
NOTOG:	CALL	SENDCHR		;send char. in 'B' to modem
	LDA	LOCFLG		;using the local mode?
	ORA	A
	JNZ	LTYPE		;if yes, show the character
	LDA	ECHOFLG		;in echo mode?
	ORA	A
	JZ	TERML		;if not, see if it was a 'CR'
;
LTYPE:	MOV	A,B		;get the char. back
	CALL	TYPE		;show on the local crt
	CALL	CHKSAVE		;to store local if buffer open
	CALL	CHKPRNT		;put on printer if running
;
CHKCR:	MVI	A,CR
	CMP	B
	JNZ	TERML		;if not cr, all done
	LDA	ECHOFLG		;in echo mode now?
	ORA	A
	JNZ	CHKLF		;if yes add a line feed
	LDA	ADDLF		;going to add a line feed in 'L' mode?
	ORA	A
	JZ	TERM		;if not, exit
;
CHKLF:	MVI	B,LF
	JMP	NOTOG		;send locally and to remote
;.....
;
;
;
TERML:	CALL	RCVREADY	;character on the receive-ready line?
	JNZ	TERM		;if not, exit
	CALL	IN$MODDATP	;get the character
	ANI	7FH		;strip parity
	JZ	TERM		;don'T BOTHER WITH NULLS
	MOV	B,A		;store temporarily
	LDA	IGNORCTL	;ignoring all but necessary ctl-chars?
	ORA	A
	JZ	GIVLF		;if zero, display them all
	MOV	A,B
	CPI	' '
	JNC	GIVLF		;display all printing characters
	CPI	'G'-40H		;^g for bell
	JC	TERM		;ignore ctl-characters less than ^g
	CPI	CR+1
	JNC	TERM		;ignore ctl-charsacters more than ^m
;
GIVLF:	MOV	A,B		;get the character back
	CALL	TYPE		;show it on the crt
	CALL	CHKSAVE		;saving for disk file?
	CALL	CHKPRNT		;printer running?
	LDA	ECHOFLG		;going to echo the character?
	ORA	A
	JZ	NOECH		;if not, do not resend
;
GIVLF1: CALL	SENDCHR		;send char. in 'B' to modem
;
NOECH:	MVI	A,CR
	CMP	B		;was it a 'CR' just now?
	JNZ	TERM		;if not, all done so exit
	LDA	ECHOFLG		;in the echo mode?
	ORA	A
	JZ	TERM
	CALL	SENDNOW		;modem ready for a character?
	MVI	B,LF
	JMP	GIVLF		;send lf
;.....
;
;
; See if putting character into memory for a disk file
;
CHKSAVE:
	LDA	SAVEFLG		;saving to disk?
	ORA	A
	RZ			;if not, exit
;
	LHLD	HLSAVE		;get last address
	MOV	M,B		;store this character
	INX	H		;increment for next character
	SHLD	HLSAVE		;remember that location
	MVI	A,LF
	CMP	B		;this character a line feed?
	JNZ	CHKSAVE1	;..type ";" after each line feed..
	MVI	A,CR		;insure at left column with a lf
	CALL	TYPE
	CALL	TYPESCLN	;show a ';' on crt
;
CHKSAVE1:
	MOV	A,H
	LXI	H,BUFTOP	;get the address at top of buffer
	CMP	H
	CZ	D$CTL$S		;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.
;
D$CTL$S:CALL	SENDNOW		;modem ready for a character?
	MVI	A,XOFF		;send a ctl-s to stop remote computer
	CALL	OUT$MODDATP
	CALL	CHKPRNT		;insure character gets to the printer
	LXI	H,BUFFDSK	;address of aux. buffer
	CALL	GETDSK		;put any extra chars. into aux. buffer
	PUSH	D		;save the number of aux. chars.
	MVI	A,1		;show we put something in the buffer..
	STA	WRFLG		;..to protect erasing an empty file
	LHLD	HLSAVE		;find current end of buffer
	CALL	WRTDSK1		;write the records
	POP	D		;get aux. char. count back
	LXI	H,BUFFER	;start again at bottom of buffer
	XRA	A		;set 'A' to zero
	CMP	D		;see if any count in 'D'
	JZ	D$CTL$Q		;if nothing, exit and continue
	LXI	B,BUFFDSK	;address of aux. buffer
;
;
; Move the characters from the auxiliary buffer to the main buffer and
; display
;
D$CTL$S1:
	LDAX	B		;get the character there
	MOV	M,A		;store in main buffer
	CALL	TYPE		;show on crt
	PUSH	H
	PUSH	D
	PUSH	B
	PUSH	PSW
	MOV	B,A
	CALL	CHKPRNT
	POP	PSW		;get the character again
	POP	B
	POP	D
	POP	H
	CPI	LF
	CZ	TYPESCLN
	INX	H		;next buffer position
	INX	B		;next aux. buffer position
	DCR	D		;one less to go
	JNZ	D$CTL$S1	;if not zero, keep going
	MVI	B,0		;falls through to 'CHKPRNT' next
;
D$CTL$Q:SHLD	HLSAVE		;next position to store in buffer	
	CALL	SENDNOW
	MVI	A,XON		;allow remote input to continue
	JMP	OUT$MODDATP
;.....
;
;
; Gets any incoming characters after sending an XOFF and stores at HL.
; Returns with number of characters stored in D-reg.
;
GETDSK:	MVI	D,0		;character count in buffer
	MVI	E,128		;maximum for buffer length
;
GETDSK1:CALL	JMP$INMODEM	;get any character
	RC			;if none, finished
	CPI	' '
	JNC	GETDSK2		;if greater, handle normally
	CPI	CR+1		;ignore ctl-chars. > cr
	JNC	GETDSK1
;
GETDSK2:MOV	M,A		;store
	INX	H		;next buffer location to use
	INR	D		;increment character count
	DCR	E		;room for one less
	JNZ	GETDSK1		;if room in buffer, keep going
	RET			;if buffer is filled, exit
;.....	
;
;
; See if printing the character, if yes, put into buffer
;
CHKPRNT:LDA	LISTFLG		;printer in use?
	ORA	A
	RZ			;return if not
;
	LHLD	HLSAVE1		;get input address
	MOV	M,B		;save this character there
	INX	H		;increment the buffer location
	SHLD	HLSAVE1		;store for next character		
	LDA	MAXRAM		;see if at top of buffer yet
	CMP	H
	CZ	P$CTL$S		;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.
;
P$CTL$S:CALL	SENDNOW		;wait until modem is ready
	MVI	A,XOFF		;send a ctl-s to stop remote computer
	CALL	OUT$MODDATP
	LXI	H,BUFFPNT	;address of aux. buffer
	CALL	GETDSK		;put any extra chars. into aux. buffer
	MOV	A,D		;get the aux. buffer char. count
	STA	DSTORE		;save for later
	RET
;.....	
;
;
; Output has now caught up to the input and both are at top of buffer
;
P$CTL$S1:
	LDA	DSTORE		;get the aux. buffer char. count
	MOV	D,A		;put into 'D' reg.
	XRA	A		;set 'A' to zero
	CMP	D		;see if any count in 'D'
	LXI	H,PBUFF		;address at start of printer buffer
	JZ	P$CTL$Q		;if nothing, exit and continue
	LXI	B,BUFFPNT	;address of aux. buffer
;
;
; Move the characters from the aux. buffer to the main buffer and display
;
P$CTL$S2:
	LDAX	B		;get the character there
	MOV	M,A		;store in main buffer
	CALL	TYPE		;show on crt
	PUSH	H
	PUSH	D
	PUSH	B
	PUSH	PSW
	MOV	B,A
	CALL	CHKSAVE
	POP	PSW
	POP	B
	POP	D
	POP	H
	CPI	LF
	CZ	TYPESCLN
	INX	H		;next buffer position
	INX	B		;next aux. buffer position
	DCR	D		;one less to go
	JNZ	P$CTL$S2	;if not zero, keep going
;
P$CTL$Q:SHLD	HLSAVE1		;next position to store in buffer
	LXI	H,PBUFF		;start of buffer location
	SHLD	HLSAVE2		;output to start of buffer
	CALL	SENDNOW		;wait until modem is ready
	MVI	A,XON		;send start character..
	JMP	OUT$MODDATP	;..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..
	ORA	A		;by 'INITADR' routine
	RZ			;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
	RZ
;
;
; If not the same, print the character
;
GOLIST1:MOV	C,M		;get character
	INX	H		;increment pointer
	SHLD	HLSAVE2		;increment for next position
	CALL	LISTER		;go print the character
;
;
; 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.
;
	LDA	MAXRAM		;check for end of buffer area
	CMP	H
	JZ	P$CTL$S1	;if at end, restore aux. 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
	RNZ			;if not, back to work
	LXI	H,PBUFF		;if output caught input, reset both..
	SHLD	HLSAVE1		;..to bottom of buffer to start over
	SHLD	HLSAVE2
	RET
;.....
;
;
; Compare the input and output pointers to see if the same address
;
CMP$I$O:LHLD	HLSAVE1		;get input pointer address
	XCHG			;put in 'DE'
	LHLD	HLSAVE2		;get output pointer address
	MOV	A,H
	CMP	D
	RNZ			;return if different
	MOV	A,L
	CMP	E
	RET
;.....
;
;
GETMAX:	LDA	SAVCCP		;going to save 'CCP'?
	ORA	A
	LDA	BDOS+2		;'MSP' of 'BDOS' address
	JZ	GETMAX1
	SBI	8		;'CCP' is 2k or 8 pages
;
GETMAX1:STA	MAXRAM		;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.
;
INMODEM:
	PUSH	H
	LXI	H,63		;about 100 milliseconds
	CALL	FIXCNT
	MOV	B,H		;delay is in 'HL'
	MOV	C,L		;transfer to 'BC'
	POP	H		;get original value of 'HL' back
;
INMODEM1:
	CALL	RCVREADY	;see if there is a character ready
	JZ	IN$MODDATP	;get the character, then return
	DCX	B		;otherwise keep timing
	MOV	A,B
	ORA	C
	JNZ	INMODEM1	;loop until timeout if needed
	STC			;shows a timeout occured
	RET
;.....
;
;
; Send a space tone to the phone line for a short time.
;
BREAK:	LDA	PMMIMODEM	;using the pmmi modem?	
	ORA	A
	JZ	BREAK1		;if not, exit
	LDA	MODCTLB		;get the last modem control byte
	ANI	BRKMSK		;set the transmit break bit low
	CALL	OUT$MODCTL2	;send it to the modem
	MVI	B,2
	CALL	TIMER		;send a space tone for 200 ms.
	LDA	MODCTLB		;get the last modem control byte
	CALL	OUT$MODCTL2	;restore to normal
	JMP	TERM		;back to work
;...
;
;
BREAK1:	CALL	JMP$BREAK	;get the user'S BREAK ROUTINE
	JMP	TERM		;back to work	
;.....
;
;
;=======================================================================
;		     WRITE BUFFER TO DISK
;
; Make sure this record is included in the count.
;
WRTDSK:	LHLD	HLSAVE
	MVI	M,EOFCHAR	;ascii file, store end-of-file char.
	LXI	D,127
	DAD	D
;
WRTDSK1:LXI	D,-(BUFFER)	;subtract the start of the buffer..
	DAD	D		;by adding a minus number to buffer end
	MOV	A,L		;divide hl by 128..
	ORA	A
	RAL			;..to get the..
	MOV	L,H		;..number of records
	MVI	H,0
	PUSH	PSW
	DAD	H
	POP	PSW
	MVI	A,0
	ADC	L
	MOV	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.
;
	LXI	D,BUFFER
	LDAX	D
	CPI	EOFCHAR		;'EOF' in first address means..
	JNZ	WRTDSK2		;..nothing in buffer to write
	LDA	WRFLG		;first time by this way?
	ORA	A
	JZ	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'.
;
WRTDSK2:MVI	C,SETDMA
	CALL	BDOSRT
	PUSH	D
	MVI	C,WRITE
	LXI	D,FCB3		;location of filename to write to
	CALL	BDOSRT
	POP	D
	ORA	A
	JNZ	WRERR		;error if disk is full
	XCHG			;put the current address in 'HL'..
	PUSH	D		;..and number of records left in..
	LXI	D,128		;..for now
	DAD	D		;add for next record write, now in 'HL'
	POP	D		;restore number of records left
	XCHG			;records to 'HL' again, address to 'DE'	
	DCX	H		;one less record left
	MOV	A,H
	ORA	L		;done writing when 'H' and 'H' both zero
	JNZ	WRTDSK2		;otherwise do another disk write
	RET
;.....
;
;
; Error while writing a record, show why it is aborting
;
WRERR:	MVI	C,CANCEL	;send cancel char. to sending station
	CALL	SEND
	CALL	CLOSFIL		;close the current file
	CALL	ERXIT		;also will reset stack
	DB	'++ DISK IS FULL, SAVING PARTIAL FILE ++','$'
;.....
;
;
; If no data to store on the disk, close the empty file and erase it
;
NOWRITE:CALL	CLOSFIL		;close the empty file
	CALL	NOASK		;erase the empty file
	CALL	ILPRT
	DB	'++ Nothing to save, erasing the file ++'
	DB	CR,LF,BELL,0
	JMP	DONETCA		;reset any flags, return to menu
;.....
;
;
; Show you are in memory-save for disk write
;
TYPESCLN:
	MVI	A,';'
	JMP	TYPE		;show on crt, return
;.....
;
;
; Save the registers, call BDOS then restore the registers
;
BDOSRT:	PUSH	B
	PUSH	D
	PUSH	H
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	RET
;.....
;
;
INITFCB:MVI	M,0		;entry at +2 will leave drive no. intact
	INX	H		;will initialize an fcb..
	MVI	B,11		;..pointed to by hl-reg. fills 1st pos
;
LOOP11:	MVI	M,' '		;..with 0, next 11 with..
	INX	H		;..with blanks, and last..
	DCR	B		;..21 with nulls.
	JNZ	LOOP11
	MVI	B,21
;
LOOP21:	MVI	M,0
	INX	H
	DCR	B
	JNZ	LOOP21
	RET
;.....
;
;
; Scans CMDBUF coutning names and putting delimiter (space) after last
; name
;
SCAN:	PUSH	H
	LXI	H,NAMECT
	MVI	M,0
	LXI	H,CMDBUF+1	;find end of cmd line..
	MOV	C,M		;..and put space there.
	MVI	B,0
	LXI	H,CMDBUF+2
	DAD	B
	MVI	M,' '
	LXI	H,CMDBUF+1
	MOV	B,M
	INR	B
	INR	B
;
SCANLP1:INX	H
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	' '
	JNZ	SCANLP1
;
SCANLP2:INX	H		;eat extra spaces
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	' '
	JZ	SCANLP2
	SHLD	BGNMS		;save start of names in cmdbuf
	INR	B
	DCX	H
;
SCANLP3:INX	H
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	' '
	JNZ	SCANLP3
	LDA	NAMECT		;counts names
	INR	A
	STA	NAMECT
;
SCANLP4:INX	H		;eat spaces
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	' '
	JZ	SCANLP4
	JMP	SCANLP3
;.....
;
;
DNSCAN:	MVI	M,' '		;space after last char
	POP	H
	RET
;.....
;
;
; Places next name in buffer so 'CMDLINE' may parse it
;
TRTOBUF:LHLD	BGNMS
	MVI	B,0
	LXI	D,FCBBUF+2
;
TBLP:	MOV	A,M
	CPI	' '
	JZ	TRBFEND
	STAX	D
	INX	H
	INX	D
	INR	B		;count chars in name
	JMP	TBLP
;.....
;
;
TRBFEND:INX	H
	MOV	A,M		;eat extra spaces
	CPI	' '
	JZ	TRBFEND
	SHLD	BGNMS
	LXI	H,FCBBUF+1	;put # chars before name
	MOV	M,B
	RET
;.....
;
;
; In CP/M V.2, if file is R/O or SYS, it is changed to 'BAK'.
;
CKCPM2:	MVI	C,CPMVER	;bdos 12 -- version number -- cp/m 2.2?
	CALL	BDOS
	ORA	A	
	RZ
	MVI	C,SETDMA
	LXI	D,TBUF
	CALL	BDOS
	MVI	C,SRCHF
	LXI	D,FCB
	CALL	BDOS
	CPI	0FFH
	RZ
;
	CALL	GETADD
	LXI	D,9
	DAD	D		;point to r/o attribute byte
	MOV	A,M
	ANI	80H		;test most significant byte
	JNZ	MKCHG		;if set, make change
	INX	H		;check system attribute byte
	MOV	A,M
	ANI	80H
	RZ			;not $sys or $r/o attribute
	DCX	H
;
MKCHG:	LXI	D,-8
	DAD	D		;point hl to filename + 1
	LXI	D,FCB+1		;move directory name to fcb..
	MVI	B,11		;..without changing drive.
	CALL	MOVE
	LXI	H,FCB+9		;r/o attribute
	MOV	A,M
	ANI	7FH		;strip r/o attribute
	MOV	M,A
	INX	H		;system attribute
	MOV	A,M
	ANI	7FH
	MOV	M,A
	LXI	D,FCB
	MVI	C,30		;set new attributes in directory
	CALL	BDOS
;
;
; Called by 'CKBAKUP' below, return done here through 'BDOS' jump
;
PLANCHG:LXI	H,FCB		;change name to type "bak"
	LXI	D,FCB2
	MVI	B,9		;move drive and name (not type)
	CALL	MOVE
	LXI	H,75H		;start of type in fcb2
	MVI	M,'B'
	INX	H
	MVI	M,'A'
	INX	H
	MVI	M,'K'
	LXI	D,FCB2
	MVI	C,ERASE		;erase any prev backups
	CALL	BDOS
	LXI	H,FCB2		;fcb2 dr field should..
	MVI	M,0		;..0 for rename.
	LXI	D,FCB
	MVI	C,23		;rename
	JMP	BDOS
;.....
;
;
CKBAKUP:LDA	BAKUPBYTE
	ORA	A
	RZ
	MVI	C,SRCHF
	LXI	D,FCB
	CALL	BDOS
	INR	A
	RZ			;file not found
	JMP	PLANCHG		;in "ckcpm2" - ret done there
;.....
;
;
;***********************************************************************
;
;	    RECEIVE A RECORD FROM SENDING STATION
;
;***********************************************************************
;
;
RCVRECD:MVI	A,1
	STA	ERRCT
	XRA	A
	STA	ONERR
;
RCVRPT:	XRA	A		;zero accum
	STA	ERRCDE		;clear receive error code
	CALL	CKABORT		;want to stop receiving file?
	LDA	QFLG
	ORA	A
	JZ	RCVACK
	CALL	ILPRT
	DB	CR,'Awaiting # ',0
	PUSH	H		;save it
	LHLD	RECDNO		;get record number
	INX	H		;bump it
	CALL	DECOUT		;print record number in decimal
	CALL	ILPRT
	DB	' (', 0
	CALL	DHXOUT		;16-bit hex conversion and output
	CALL	ILPRT
	DB	'H) ',0
	MOV	A,L		;only low byte used by program
	POP	H		;restore it
;
;
; Message is sent so now we can send the 'ACK' for previous record, (or
; CRC or NAK for the original record to start the file).
;
RCVACK:	LDA	ACKFLG
	CALL	SEND
;	
; If CRC is in effect, there is a 10-second timeout to the first SOH.
; It then tries several 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).
;
; During this time incoming CRC and NAK characters are ignored as these
; may be the ones we are sending at each timeout, coming back from the
; full duplex system.
;
RCVSQ:	MVI	B,10-1		;10-second wait for 'SOH' or 'EOT' char.
	CALL	RECV		;..(extra 1 second from rcvserr)
	JC	RCVSTOT		;send timeout msg if no char. in 10 sec.
	CPI	SOH		;get a start of header?
	JZ	RCVSOH
	ORA	A
	JZ	RCVSQ
	CPI	CRC		;ignore our own 'CRC' char. if any
	JZ	RCVSQ		;do not count this as a timeout
	CPI	NAK		;ignore our own 'NAK' char. if any
	JZ	RCVSQ		;do not count this as a timeout
	CPI	EOT
	STC
	RZ
	MOV	B,A
	LDA	BATCHFLG	;using batch mode now?
	ORA	A
	JNZ	RCVSQ1		;if not, exit
	LDA	ONERR		;this the first error this transfer?
	ORA	A
	JNZ	RCVSQ1		;if not, handle normally
	STA	ERRCT		;reset the error cout to exclude this
	CMA			;if yes, ignore this error
	STA	ONERR		;set the flag
	JMP	RCVSERR	
;
RCVSQ1:	CALL	RCVQERR		;see if quiet mode
;
RCVSQ2:	MOV	A,B
	CALL	HEXO
	CALL	ILPRT
	DB	'H received not SOH - ',0
;
RCVPRN:	CALL	SHOWERR		;display error count
;
RCVSERR:MVI	B,1		;1 second wait after any char. is sent..
	CALL	RECV		;..to insure sender is waiting to copy
	JNC	RCVSERR 	;if still sending, ignore all chars.
	CALL	CKABORT		;want to stop receiving now?
	LDA	CRCFLAG		;get 'CRC' flag
	ORA	A		;'CRC' in effect?
	MVI	A,NAK		;put 'NAK' in accum
	JZ	RCVSER1		;no, send the 'NAK' for checksum
	LDA	FIRSTME 	;get first time switch
	ORA	A		;has first 'SOH' been received?
	MVI	A,NAK
	JNZ	RCVSER1		;yes, then send 'NAK'
	MVI	A,CRC		;tell sender 'CRC' is in effect
;
RCVSER1:STA	ACKFLG		;store the 'NAK' or 'CRC' request
	LDA	ERRCT		;increment the	error count
	INR	A
	STA	ERRCT
	CPI	10+1		;if 10 already, abort
	JC	RCVRPT		;if less than 10, keep going
	JMP	ABORT		;if yes, abort (do not ask to resend)
;.....
;
;
RCVSABT:LXI	SP,STACK	;reset the stack just in case
	CALL	CLOSFIL		;close the partial file
	CALL	NOASK		;delete partial file
	CALL	ILPRT
	DB	CR,LF,LF
	DB	'++ RECEIVED FILE CANCELLED ++',CR,LF,BELL
	DB	'++ UNFINISHED FILE DELETED ++',CR,LF,0
	JMP	DONETCA
;.....
;
;
RCVQERR:LDA	QFLG		;see if quiet mode now
	ORA	A
	RNZ			;if not, proceed normally
	POP	H		;reset stack for 'CALL'
	JMP	RCVSERR		;skip printing message
;.....
;
;
; Timeout routine each 10 seconds for no 'SOH' (Start-Of-Header)
;
RCVSTOT:MVI	A,1
	STA	ONERR
	LDA	QFLG
	ORA	A
	JZ	RCVSTOT1
	CALL	ILPRT
	DB	'++ Timeout ',0
	CALL	SHOWERR		;display the current error count
;
;
; Routine with switch from CRC to checksum if ERCNT reaches ERRCRC and
; are currently in CRC mode
;
RCVSTOT1:
	LDA	ERRCT		;get the error count
	CPI	ERRCRC		;get the 'CRC' error-limit
	JC	RCVSTOT2	;if less, keep going
	LDA	FIRSTME		;if more, have we had a 'SOH' yet?
	ORA	A
	JNZ	RCVSTOT2	;if yes, don'T SWITCH TO CHECKSUM
	LDA	CRCFLAG		;if not, see if already in checksum
	ORA	A
	JZ	RCVSTOT2	;if already checksum, exit
	XRA	A		;show in checksum now
	STA	CRCFLAG		;change from 'CRC' to checksum
	CALL	ILPRT		;...for future files
	DB	'++ Switching to CHECKSUM mode ++',CR,LF,BELL,0
;
RCVSTOT2:
	JMP	RCVSERR		;increment error count
;.....
;
;
; Get the error count and display on CRT
;
SHOWERR:PUSH	H		;save the current address
	LHLD	ERRCT		;get the current error number
	MVI	H,0		;only a 8-bit number, now in 'L' reg.
	CALL	DECOUT		;display the error in decimal
	POP	H		;restore the current address
	CALL	ILPRT
	DB	' ++',CR,LF,0	;finish the error message
	LDA	ERRCT
	CPI	10+1		;reached the limit yet?
	JNC	ABORT		;if yes, abort
	RET
;.....
;
;
; Got SOH - get block #, block # complemented for header
;
RCVSOH:	MVI	A,1
	STA	FIRSTME 	;indicate first soh recv'D
	MVI	B,1		;1-second max awaiting char.
	CALL	RECV		;get record
	JC	RCVSTOT 	;got timeout
	MOV	D,A		;store in an unused reg. for now
	MVI	B,1		;1-second max awaiting char.
	CALL	RECV		;get the complimented value next
	JC	RCVSTOT
	CMA			;invert to compare with 1st value
	CMP	D		;compare with first one sent
	JZ	RCVDATA		;they agree, go get rest of record
	CALL	RCVQERR
	CALL	ILPRT
	DB	'++ Bad header ',0
	JMP	RCVPRN
;.....
;
;
RCVDATA:MOV	A,D
	STA	RCVRNO		;store the record number they just sent
	MVI	A,1
	STA	DATAFLG
	MVI	C,0		;new record, clear 'CHECKSUM' value
	LXI	H,0
	SHLD	CRCVAL		;new record, clear 'CRC' value
	LXI	H,TBUF		;store at 0080h
;
RCVCHR:	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	MOV	M,A		;store incoming characters at 0080h
	INR	L		;location to store next character
	JNZ	RCVCHR		;becomes 0 at 128 characters
	XRA	A
	STA	DATAFLG
	LDA	CRCFLAG		;in 'CRC' mode?
	ORA	A
	JNZ	RCVCRC		;if yes, exit
	MOV	D,C
	MVI	B,1
	CALL	RECV		;get the checksum number
	JC	RCVSTOT
	CMP	D
	JNZ	RCVCERR
;
CHKSNUM:LDA	RCVRNO
	MOV	B,A
	LDA	RECDNO
	CMP	B
	JZ	RECVACK
	INR	A
	CMP	B
	JNZ	ABORT
	RET
;.....
;
;
RCVCRC: MVI	E,2		;number of crc bytes
;
RCVCRC1:MVI	B,1
	CALL	RECV
	JC	RCVSTOT		;no character, exit
	DCR	E
	JNZ	RCVCRC1
	CALL	CRCCHK		;check 'CRC' bytes of received message
	ORA	A
	JZ	CHKSNUM		;if ok, exit
	CALL	RCVQERR		;see if quiet mode
	CALL	ILPRT		;if not, show error message
	DB	'++ CRC error ',0
	JMP	RCVPRN		;show error number
;.....
;
;
RCVCERR:CALL	RCVQERR		;see if quiet mode
	CALL	ILPRT		;if not, show error message
	DB	'++ CHECKSUM error ',0
	JMP	RCVPRN		;show error number
;.....	
;
;
RECVACK:MVI	A,ACK		;got the record ok but wait to send..
	STA	ACKFLG		;..until displaying next record count
	JMP	RCVRECD
;.....
;
;
SENDHDR:LDA	QFLG
	ORA	A
	JZ	SENDHNM
	CALL	ILPRT
	DB	CR,'Sending # ',0
	PUSH	H		;store current address
	LHLD	RECDNO		;get record number
	CALL	DECOUT		;print it in decimal
	CALL	ILPRT
	DB	' (',0
	CALL	DHXOUT		;16 bit hex conversion & output
	CALL	ILPRT
	DB	'H) ',0
	POP	H		;restore current address
;
SENDHNM:MVI	A,SOH		;send 'SOH' character to the output
	CALL	SEND
	LDA	RECDNO		;send record number to the output
	CALL	SEND
	LDA	RECDNO
	CMA			;complement the record number
	JMP	SEND		;send this value to the output
;.....
;
;
SENDREC:MVI	A,1
	STA	DATAFLG
	MVI	C,0
	LXI	H,0		;new record, clear 'CHECKSUM' value
	SHLD	CRCVAL		;new record, clear 'CRC' value
	LXI	H,TBUF		;store at 0080h
;
SENDC:	MOV	A,M
	CALL	SEND
	INR	L
	JNZ	SENDC
	XRA	A
	STA	DATAFLG
	RET
;.....
;
;
SENDCKS:MOV	A,C
	JMP	SEND
;.....
;
;
SENDCRC:PUSH	H
	LHLD	CRCVAL
	MOV	A,H
	CALL	SEND
	MOV	A,L
	CALL	SEND
	POP	H
	XRA	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 sends an error message and the current record is again repeated.
; This occurs until the error limit has been reached.  If the first NAK
; is missed, it waits up to 12 seconds before declaring a 2nd error.
; This insures there is no collision with the station attempting to send
; the NAK, since it waits only 10 seconds.
;
GETACK:	MVI	B,12		;wait 12 seconds max
	CALL	RECVDG		;go wait for an 'ACK' or 'NAK' character
	JC	GETATOT		;no character, timed out
	CPI	87H		;special arapnet 'BUSY' char.
	JZ	GETACK		;ignore, do not resend, line is busy
	CPI	07H		;special mainframe 'BUSY' char.
	JZ	GETACK		;ignore, do not resend, line is busy
	CPI	ACK		;got a character, was it an 'ACK'?
	RZ			;yes, return and send next record

;
;
; If the ACKNAK option is 'YES' it will resend the sector when any char-
; acter other than ACK (or 07H, 87H) is received (including NAK).
;
	MOV	B,A		;store the character so we can use 'A'
	LDA	ACKNAK		;going to limit resending to 'NAK' only?
	ORA	A
	JNZ	GETACK1		;if not resend on any non-ack
	CPI	NAK		;was it an authentic 'NAK'?
	JNZ	GETACK		;ignore if other than 'NAK', ask again
;
GETACK1:LDA	QFLG		;in quiet mode now?
	ORA	A
	JZ	ACKERR		;if yes, do not show error on crt
	CALL	ILPRT
	DB	'++ ',0
	MOV	A,B		;if not quiet mode, get character back
	CPI	NAK		;is it a 'NAK'?
	JZ	GETACK2		;show 'NAK' in that case
	CALL	HEXO
	CALL	ILPRT
	DB	'H',0
	JMP	GETACK3
;
GETACK2:CALL	ILPRT
	DB	'NAK',0	
;
GETACK3:CALL	ILPRT		;print the error message
	DB	' received not ACK - ',0
	CALL	SHOWERR		;show the error number
;
;
; Timeout or error on ACK - bump error count then resend the record if
; error limit is not exceeded.
;
ACKERR:	LDA	ERRCT		;increment the error count
	INR	A
	STA	ERRCT
	CPI	10+1		;at error limit yet?
	RC			;if not, return
;
ACKERR1:CALL	ERXIT
	DB	CR,LF,'++ SEND-FILE CANCELLED ++','$'
;.....
;
;
; Reached error limit
;
GETATOT:CALL	ILPRT
	DB	CR,'++ TIMEOUT - no ACK - ',0
	CALL	SHOWERR		;display error count
	JMP	ACKERR
;.....
;
;
CKABORT:LDA	QFLG
	ORA	A
	RZ
	CALL	STAT
	RZ
	CALL	KEYIN
	CPI	CANCEL
	RNZ
;
;
; Aborts send or receive routines and returns to command line
;
ABORT:	LXI	SP,STACK
;
ABORTL:	MVI	B,1		;1-second delay to clear input
	CALL	RECV
	JNC	ABORTL
	MVI	A,CANCEL	;show you are cancelling
	CALL	SEND
;
ABORTW:	MVI	B,1		;1-second delay to clear input
	CALL	RECV
	JNC	ABORTW
	MVI	A,' '
	CALL	SEND
	MVI	A,'B'   	;turn multi-file mode..
	STA	BATCHFLG	;..off so routine ends.
	STA	ABORTFLG	;shows an abort was made
	XRA	A
	STA	NFILFLG		;stop copy into memory for disk file
	LDA	OPTION		;receiving a file now?
	CPI	'R'
	JZ	RCVSABT		;if yes, cancel the unfinished file
	CALL	ILPRT
	DB	CR,LF,LF,'++ FILE CANCELLED ++',CR,LF,BELL,0
	JMP	DONETCA
;.....
;
;
; Increment the record count
;
INCRRNO:PUSH	H
	LHLD	RECDNO		;get record number
	INX	H		;bump it
	SHLD	RECDNO		;store it
	MOV	A,L
	POP	H
	RET
;.....
;
;
; First check for any wild cards and disallow, just to be safe.  Do not
; want a group of files being accidently erased.
;
ERASFIL:LXI	H,FCB		;file name is stored here
	MVI	B,11		;maximum of 11 chars for filename.ext
;
ERASFIL1:
	INX	H		;next location in file name
	MOV	A,M		;get the char.
	CPI	'?'		;check for any wild card chars.
	JZ	ERRORW		;error if one is found
	DCR	B		;number of tries left
	JNZ	ERASFIL1	;if not zero, keep checking
	LDA	BATCHFLG	;don'T ASK FOR ERASE..
	ORA	A		;..in multi-file mode,..
	JZ	NOASK		;..just do it.
	LXI	D,FCB
	MVI	C,SRCHF
	CALL	BDOS
	INR	A
	RZ			;file erased ok, return
	CALL	ILPRT		;otherwise make sure it'S OK
	DB	'File exists - erase?  (Y/N): ',BELL,0
	CALL	KBDCHR
	CPI	'Y'
	JNZ	MENU		;if not a 'Y' do not erase
	CALL	CRLF		;otherwise erase the file
;
NOASK:	LXI	D,FCB
	MVI	C,ERASE
	JMP	BDOS
;.....
;
;
ERRORW: POP	H		;restore stack from "call erasfil"
	CALL	ILPRT
	DB	'++ NO WILDCARDS ALLOWED FOR TEXT FILES ++'
	DB	CR,LF,BELL,0
	JMP	MENU
;.....
;
;
BLKFILE:CALL	ILPRT		;no file named for send or receive
	DB	'++ NO FILE SPECIFIED ++',CR,LF,BELL,0
	JMP	MENU
;.....
;
;
MAKEFIL:LXI	D,FCB
	MVI	C,MAKE
	CALL	BDOS
	INR	A
	RNZ
	CALL	ERXIT
	DB	'++ ERROR -- Can''t open file ++',CR,LF
	DB	'++ Directory is perhaps full ++','$'
;
CNREC:	MVI	C,FILSIZ	;compute file size function in cp/m 2.x
	LXI	D,FCB		;point to file control block
	CALL	BDOS
	LHLD	FCB+33		;get record count
	SHLD	RCNT		;store it
	LXI	H,0		;zero 'HL'
	SHLD	FCB+33		;reset random record in fcb
	RET
;
;.....
;
;
OPENFIL:XRA	A
	STA	FCBEXT
	LXI	D,FCB
	MVI	C,OPEN
	CALL	BDOS
	INR	A
	JNZ	SENDTIME	;send transfer time, # of records, etc.
	CALL	ERXIT		;file did not open
	DB	'++ FILE NOT FOUND ++','$'
;.....
;
;
CLOSFIL:LXI	D,FCB		;get the file name
	MVI	C,CLOSE
	CALL	BDOS		;close the file
	INR	A
	RNZ
	JMP	ERXIT1		;no file to close, exit
;.....
;
;
; Update record read
;
RDRECD:	LDA	RECINBF		;decrement 'RECORDS IN BUFFER' count
	DCR	A
	STA	RECINBF
	JM	RDBLOCK
	LHLD	RECPTR		;find where last move stopped
	LXI	D,128
	CALL	MOVE128		;move 128 characters
	SHLD	RECPTR		;store new address for next move
	RET
;.....
;
;
; Buffer empty so read in another block (up to 16k or 128 records) from
; disk
;
RDBLOCK:LDA	EOFLG
	CPI	1
	STC
	RZ
	MVI	C,0
	LXI	D,BUFFER
;
RDRECLP:PUSH	B
	PUSH	D
	MVI	C,SETDMA
	CALL	BDOS
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	POP	D
	POP	B
	ORA	A
	JZ	RDRECOK
	DCR	A
	JZ	REOF
	CALL	ERXIT
	DB	'++ FILE READ ERROR ++','$'
;
RDRECOK:LXI	H,128
	DAD	D
	XCHG
	INR	C
	MOV	A,C
	CPI	BUFSIZ*8	;buffer size in 128 byte records
	JZ	RDBFULL
	JMP	RDRECLP
;...
;
;
REOF:	MVI	A,1
	STA	EOFLG
	MOV	A,C
;
;
; Buffer full or rceived "End Of File (EOF)"
;
RDBFULL:STA	RECINBF
	LXI	H,BUFFER
	SHLD	RECPTR
	MVI	C,SETDMA
	LXI	D,TBUF
	CALL	BDOS
	JMP	RDRECD
;.....
;
;
; Write a record
;
WRRECD:	LHLD	RECPTR
	XCHG
	LXI	H,128
	CALL	MOVE128
	XCHG
	SHLD	RECPTR		;new record pointer
	LDA	RECINBF		;increment 'RECORDS IN BUFFER' count
	INR	A
	STA	RECINBF
	CPI	BUFSIZ*8	;buffer size in 128 byte records
	RNZ			;buffer not full, return
;
;
; Write a 16k block to disk (128 RECORDS)
;
WRBLOCK:LDA	RECINBF		;get the number of records in the buffer
	ORA	A
	RZ			;if zero, don'T TRY TO MOVE TO DISK
	MOV	C,A		;otherwise store in 'C' reg.
	LXI	D,BUFFER	;start of buffer to move to disk
;
DSKWRT:	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,SETDMA
	CALL	BDOS
	MVI	C,WRITE
	LXI	D,FCB
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	ORA	A
	JNZ	WRERR		;error if disk is full
	LXI	H,128		;add in another page
	DAD	D
	XCHG
	DCR	C		;one less record left to move to disk
	JNZ	DSKWRT
	XRA	A
	STA	RECINBF		;zero the 'RECORDS IN BUFFER' count
	LXI	H,BUFFER	;reset location to next buffer start
	SHLD	RECPTR
	RET
;.....
;
;
;----> RECV: Receive a character
;
; Timeout time is in B, in seconds.  Entry via 'RECVDG' deletes garbage
; characters on the line.  For example, having just sent a sector,
; calling RECVDG will delete any line noise induced characters LONG
; before the ACK/NAK would be received.  Returns with character in 'A'.
;
RECVDG:	CALL	CKCHAR		;catch any garbage characters
;
RECV:	PUSH	D		;save any current values
	MOV	A,B		;multiply 'B' by 4 for extra delay
	RAL
	RAL
	MOV	B,A
;
MSEC:	CALL	CKABORT		;want to intentionally abort?
	PUSH	H		;save current 'HL'
	LXI	H,175		;master delay factor
	CALL	FIXCNT
	XCHG			;put delay into 'DE'
	POP	H		;restore current 'HL'
;
MWTI:	CALL	RCVREADY	;input have a character ready?
	JZ	MCHAR		;if yes, exit
	DCR	E
	JNZ	MWTI
	DCR	D
	JNZ	MWTI
	DCR	B		;number of seconds wanted
	JNZ	MSEC		;if not zero, do 1-second loop again
	POP	D		;restore original values
	STC			;no character so set carry bit
	RET
;.....
;
;
MCHAR:	CALL	IN$MODDATP
	POP	D		;restore original values
	PUSH	PSW
	CALL	CRCUPD		;calculate 'CRC'
	ADD	C	
	MOV	C,A
	LDA	RSEEFLG
	ORA	A
	JZ	MONIN
	LDA	VSEEFLG
	ORA	A
	JNZ	NOMONIN
	LDA	DATAFLG
	ORA	A
	JZ	NOMONIN
;
MONIN:	POP	PSW
	PUSH	PSW
	CALL	SHOW
;
NOMONIN:POP	PSW
	ORA	A
	RET
;.....
;
;
; Send a character to the modem
;
SEND:	PUSH	PSW
	LDA	SSEEFLG
	ORA	A
	JZ	MONOUT
	LDA	VSEEFLG
	ORA	A
	JNZ	NOMONOT
	LDA	DATAFLG
	ORA	A
	JZ	NOMONOT
;
MONOUT:	POP	PSW
	PUSH	PSW
	CALL	SHOW
;
NOMONOT:POP	PSW
	PUSH	PSW
	CALL	CRCUPD		;update the 'CRC' calcuation
	ADD	C
	MOV	C,A
;
SENDW:	CALL	SENDRDY
	JNZ	SENDW
	POP	PSW
	JMP	OUT$MODDATP	;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 100 seconds
; before timeout.  This gives the receiving station ample time to name
; a file, etc.
;
WAITNAK:CALL	ILPRT
	DB	'Waiting ready signal',CR,LF,0
;
WAITNLP:CALL	CKABORT
	MVI	B,1
	CALL	RECV
	CPI	CANCEL		;want to quit?
	JZ	ABORT
	CPI	CRC		;crc request?
	JZ	WAITCRC		;yes, go set 'CRC' flag
	CPI	NAK
	JZ	WAITCHK
	DCR	E
	JNZ	WAITNLP
	JMP	ABORT
;...
;
;
WAITCRC:CALL	ILPRTQ
	DB	'CRC request received',CR,LF,0
	MVI	A,1
	STA	CRCFLAG		;make sure in 'CRC' mode then
	RET
;.....
;
;
WAITCHK:LDA	BATCHFLG	;in batch mode?
	ORA	A
	RZ
	CALL	ILPRTQ
	DB	'Got checksum request',CR,LF,0
	RET
;
WAITCHK1:
	CALL	ILPRTQ
	DB	'Name NAK received',CR,LF,0
	RET
;.....
;
;
; Finished with the file transfer
;
DONE:	LDA	BATCHFLG	;in batch mode?
	ORA	A
	JNZ	DONETC		;exit if not
	LDA	QFLG
	ORA	A
	JZ	NMSTRNS
	MVI	B,12		;zero out ftrnmsg
	LXI	H,FTRNMSG
	MVI	A,0
;
ZEROLP:	MOV	M,A
	INX	H
	DCR	B
	JNZ	ZEROLP
	MVI	B,12		;put file name in ftrnmsg
	LXI	H,FCB+1		
	LXI	D,FTRNMSG
;
LOADMSG:MVI	A,4		;start of file type?
	CMP	B
	JZ	PERIOD		;put in period if so
	MOV	A,M	
	CPI	' '		;don'T PUT IN SPACE
	JZ	SKPSP	
	STAX	D		;store in ftrnmsg
	INX	D
;
SKPSP:	INX	H
	DCR	B
	MOV	A,B
	ORA	A		;end of file name?
	JZ	FTRNMSG0	;display file name
	JMP	LOADMSG		;loop for another character
;.....
;
;
PERIOD:	MOV	A,M
	CPI	' '		;is file type empty?
	JZ	FTRNMSG0	;go if so
	MVI	A,'.'		;else put period in message
	STAX	D
	INX	D
	DCR	B
	JMP	LOADMSG
;.....
;
;
FTRNMSG0:
	CALL	ILPRT
	DB	CR,LF
;
FTRNMSG:DS	12
	DB	0
	CALL	ILPRT
	DB	' Transferred',CR,LF,LF,BELL,0
;
NMSTRNS:LDA	FCB		;save drive no.
	STA	DISKNO
	LXI	H,FCB		;blank out file control blocks
	CALL	INITFCB
	LDA	DISKNO		;put drive number back
	STA	FCB
	LXI	H,RESTSN	;restore record numbers..
	LXI	D,RECDNOB	;..for new file transfer.
	MVI	B,RECDNOE-RECDNOB ;routine also done in menu.
	CALL	MOVE
	CALL	SENDNOW		;insures last character is finished
	CALL	CKCHAR		;catch any echo characters on line
	LDA	SENDFLG		;goes to either send or..
	ORA	A		;..receive file, depending..
	JNZ	SENDFIL2	;..upon which routine set..
	JMP	RCVFIL1		;..the flag in multi-file mode.
;.....
;
;
DONETC:	CALL	CKABORT		;slight delay for next message
	CALL	ILPRT
	DB	CR,LF,'[Transfer Completed]',CR,LF,BELL,0
;
DONETCA:LDA	XITFLG		;special 'X' flag set?
	ORA	A
	JZ	BYEBYE		;if yes, disconnect and reboot
	LDA	DISCFLG		;normal 'D' flag set?
	ORA	A
	JZ	DONETCD		;if yes, disconnect, get next command
;
DONETCB:CALL	JMP$NOPARITY	;reset to no parity
	XRA	A
	STA	CRCFLAG		;reset back to checksum
	STA	FIRSTME		;reset first-time 'SOH' flag
	STA	FSTFLG		;reset multi-file trans
	STA	NFILFLG		;turn off the memory save for disk file
	STA	SAVEFLG		;stop memory save in term routine.
	LDA	VSEEFLG		;view flag set?
	ORA	A
	JNZ	DONETCC		;if not, exit
	CMA
	STA	QFLG		;vseeflg also sets the qflg
	STA	VSEEFLG		;reset the flag
;
DONETCC:LXI	H,QFLG		;in quiet mode?
	MOV	A,M
	ORA	A
	MVI	M,'Q'		;reset the flag to normal
	JZ	MENU		;if yes, go back to command line
	LDA	ABORTFLG	;come here from a timeout?
	ORA	A
	JNZ	MENU		;if yes, go to command mode
	LDA	TERMFLG		;see if return to..
	ORA	A		;..terminal mode..
	JNZ	MENU		;..after x'FER.
	CALL	CRLF
	JMP	TERM
;.....
;
;
DONETCD:CALL	ILPRT
	DB	CR,LF,'<< DISCONNECTED >>',BELL,CR,LF,0
	CALL	JMP$GOODBYE	;set 'DTR' low for 300 ms.
	LDA	PMMIMODEM
	ORA	A
	CNZ	JMP$DISCONNT
	LDA	SMARTMODEM
	ORA	A
	CNZ	JMP$DISCONNT
	JMP	MENU0		;back to command line
;.....
;
;
MOVEFCB:LXI	H,FCB+16
	LXI	D,FCB
	MVI	B,16
	CALL	MOVE
	XRA	A
	STA	FCBSNO
	STA	FCBEXT
	RET
;.....
;
;
SHOW:	CPI	LF
	JZ	CTYPE
	CPI	CR
	JZ	CTYPE
	CPI	9
	JZ	CTYPE
	CPI	' '
	JC	SHOWHEX
	CPI	7FH
	JC	CTYPE
;
SHOWHEX:PUSH	PSW
	MVI	A,'('
	CALL	CTYPE
	POP	PSW
	CALL	HEXO
	MVI	A,')'
	JMP	CTYPE
;.....
;
;
CTYPE:	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A
	MVI	C,WRCON
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	RET
;.....
;
;
CRLF:	PUSH	PSW
	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
	CALL	TYPE
	POP	PSW
	RET
;.....
;
;
STAT:	PUSH	B
	PUSH	D
	PUSH	H
;
VSTAT:	CALL	$-$		;bios constat address, filled in..
	POP	H		;..by 'INITADR' routine
	POP	D
	POP	B
	ORA	A
	RET
;.....
;
;
KEYIN:	PUSH	B
	PUSH	D
	PUSH	H
;
VKEYIN:	CALL	$-$		;bios conin address, filled in..
	POP	H		;..by 'INITADR' routine
	POP	D
	POP	B
	RET
;.....
;
;
LISTER:	PUSH	B
	PUSH	D
	PUSH	H
;
VLIST:	CALL	$-$		;bios list out address, filled in..
	POP	H		;..by 'INITADR' routine
	POP	D
	POP	B
	RET
;.....
;
;
TYPE:	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A
;
VTYPE:	CALL	$-$		;bios conout address, filled in..
	POP	H		;..by initadr routine
	POP	D
	POP	B
	POP	PSW
	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:	CPI	61H		;changes lower case character..
	RC			;..in a-reg to upper case.
	CPI	7AH+1		;see if more than small 'Z'
	RNC
	ANI	5FH
	RET
;.....
;
;
DECOUT:	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	LXI	B,-10
	LXI	D,-1
;
DECOU1:	DAD	B
	INX	D
	JC	DECOU1
	LXI	B,10
	DAD	B
	XCHG
	MOV	A,H
	ORA	L
	CNZ	DECOUT
	MOV	A,E
	ADI	'0'
	CALL	CTYPE
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET
;.....
;
;
;---->	DHXOUT: - double precision hex output routine
;
DHXOUT:	PUSH	H
	PUSH	PSW
	MOV	A,H		;get ms byte
	CALL	HEXO		;output high order byte
	MOV	A,L		;get ls byte
	CALL	HEXO		;output low order byte
	POP	PSW
	POP	H
	RET
;.....
;
;
; Prints a hex value in 'A' on the CRT
;
HEXO:	PUSH	PSW
	RAR
	RAR
	RAR
	RAR
	CALL	NIBBL
	POP	PSW
;
NIBBL:	ANI	0FH
	CPI	10
	JC	ISNUM
	ADI	7
;
ISNUM:	ADI	'0'		;add in ascii bias
	JMP	CTYPE
;.....
;
;
; Displays the control-characters shown in the menu
;
SHFTYPE:PUSH	PSW
	CALL	ILPRT
	DB	'CTL-',0
	POP	PSW
	ADI	40H		;convert binary to ascii chars.
	CALL	TYPE		;show on the crt
	JMP	ILPRT
;.....
;
;
; Write a string of characters to the CRT
;
ILPRT:	XTHL
;
ILPRT1:	MOV	A,M		;get the character
	ORA	A		;see if a "0" for end of string
	JZ	ILPRT2		;if yes, all done
	CALL	CTYPE		;show on crt
	INX	H		;get the next location in the string	
	JMP	ILPRT1
;
ILPRT2:	XTHL			;restore the address
	RET
;.....
;
;
; Write a string of characters unless in the Quiet mode
;
ILPRTQ:	XTHL
;
ILPRTQ1:MOV	A,M		;get the character
	ORA	A		;see if a "0" for end of string
	JZ	ILPRTQ2		;if yes, all done
	LDA	QFLG
	ORA	A
	MOV	A,M
	CNZ	CTYPE		;show on crt if not in quite mode
	INX	H		;get the next location in the string	
	JMP	ILPRTQ1
;
ILPRTQ2:XTHL			;restore the address
	RET
;.....
;
;
PRTMSG:	MVI	C,PRINT		;print the string
	JMP	BDOS
;.....
;
;
; Displays error statement then resturns to command mode
;
ERXIT:	POP	D
	CALL	PRTMSG
	MVI	A,BELL
	CALL	TYPE
	CALL	CRLF
;
ERXIT1:	MVI	A,1
	STA	ABORTFLG	;shows an unintentional abort
	LDA	BATCHFLG	;in batch mode?
	ORA	A
	JNZ	DONETCB		;if not, exit
	JMP	ABORT		;abort other computer
;.....
;
;
; Exits directly to CP/M, with no reboot unless you have selected pos-
; sible overwriting of 'CCP'
;
EXIT:	LDA	OLDUSER		;get original user number back
	MOV	E,A
	CALL	SETUSER
	MVI	C,SETDMA
	LXI	D,TBUF		;restore original buffer area
	CALL	BDOS
	MVI	B,2		;wait to insure last char. finished
	CALL	TIMER
	CALL	CKCON		;catch any keyboard characters
	LDA	NFILFLG		;saving for a disk file?
	ORA	A
	CNZ	WRTFIL1		;if yes, close the file
	LDA	SAVCCP		;was 'CCP' left intact?
	ORA	A
	JZ	0000H		;if not, warm reboot just in case
;
EXIT1:	XRA	A		;clear the 'A' reg. and all flags
	LHLD	STACK		;get the original stack pointer back
	SPHL			;set the stack pointer to that address
	RET
;.....
;
;
; Catch any extra keyboard characters coming through BDOS
;
CKCON:	MVI	C,CONST		;see if any characters waiting
	CALL	BDOS
	ORA	A
	RZ			;if not, exit
	MVI	C,RDCON		;otherwise get for the character
	CALL	BDOS
	XRA	A		;discard the character
	JMP	CKCON		;see if any others
;.....
;
;	
MOVE128:MVI	B,128
;
MOVE:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	MOVE
	RET
;.....
;
;
; Sends the digit to the Smartmodem
;
SENDCHR:CALL	SENDNOW		;wait until modem is ready for character
	MOV	A,B		;get the original character back
	JMP	OUT$MODDATP	;send the character to modem, return
;.....
;
;
; Initializes CP/M file control blocks AT 5CH and 6CH
;
SETFCB:	LXI	D,CMDBUF
	LXI	H,FCB
	JMP	CMDLINE
;.....
;
;
; Adjusts loop counter for the selected clock speed.  Returns with delay
; in 'HL'.
;
FIXCNT:	LDA	CLOCK		;get the user'S CLOCK SPEED
	PUSH	D		;save the current 'DE' value
	PUSH	H
	POP	D		;get same value into 'DE' as in 'HL'
;
CNTMUL:	DAD	D		;add 'DE' to 'HL'
	DCR	A		;one less to go
	JNZ	CNTMUL
	POP	D		;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	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	CALL	INITIAL		;fills fcbs with blanks and nulls
	XCHG			;get start of command line in hl.
	INX	H		;address # bytes in cmd line.
	MOV	E,M		;load de pair with # bytes.
	MVI	D,0
	INX	H
	DAD	D		;point to byte after last char..
	MVI	M,CR		;..in cmd line and store delimiter.
	POP	H		;restore hl and de.
	POP	D
	PUSH	D
	PUSH	H
	INX	D		;address start of command.
	INX	D
	CALL	DRIVE
;
NAME1:	MVI	C,8		;transfer first filename to fcb.
	CALL	TRANS
	CPI	CR
	JZ	DONEL
	CPI	' '		;if space, then start of..
	JZ	NAME2		;..second filename.
	POP	H		;filetype must be after..
	PUSH	H		;..eighth byte of name.
	LXI	B,9
	DAD	B
	MVI	C,3		;transfer type of first file
	CALL	TRANS
	CPI	CR
	JZ	DONEL
;
NAME2:	LDAX	D		;eat multiple spaces..
	CPI	' '		;..between names.
	JNZ	NAME3
	INX	D
	JMP	NAME2
;
NAME3:	POP	H		;second name starts in 16th byte.
	PUSH	H		;point hl to this byte.
	LXI	B,16
	DAD	B
	CALL	DRIVE
	MVI	C,8
	CALL	TRANS
	CPI	CR
	JZ	DONEL
	POP	H		;second type starts in 25th byte.
	PUSH	H
	LXI	B,25
	DAD	B
	MVI	C,3
	CALL	TRANS
;
DONEL:	POP	H
	PUSH	H
	INX	H		;point to 1st char of 1st name in fcb..
	CALL	SCANL		;check for * (ambiguous names).
	POP	H
	PUSH	H
	LXI	B,17		;..to 1st char of second name in fcb.
	DAD	B
	CALL	SCANL
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET
;.....
;
;
; Subroutines for CMDLINE section
;
INITIAL:PUSH	H	;initializes fcb with 1 null (for first drive)..
	PUSH	B	;..11 blanks, 4 nulls, 1 null (for 2nd drive)..
	MVI	M,0	;..11 blanks, and 4 nulls.
	INX	H
	MVI	B,11
	MVI	A,' '
	CALL	INITFILL
	MVI	B,5
	XRA	A
	CALL	INITFILL
	MVI	B,11
	MVI	A,' '
	CALL	INITFILL
	MVI	B,4
	XRA	A
	CALL	INITFILL
	POP	B
	POP	H
	RET
;.....
;
;
INITFILL:
	MOV	M,A
	INX	H
	DCR	B
	JNZ	INITFILL
	RET
;.....
;
;
DRIVE:	INX	D		;check 2nd byte of filename.  if it..
	LDAX	D		;..is a ":", then drive was specified..
	DCX	D
	CPI	':'
	JNZ	DEFDR		;..else zero for default drive ..
	LDAX	D		;..('INIT' put zero)
	ANI	5FH
	SUI	40H		;calculate drive (a=1, b=2,...)..
	MOV	M,A		;..and place it in fcb.
	INX	D		;address first byte of..
	INX	D		;..in cmd line,..
;
DEFDR:	INX	H		;..and name field in fcb.
	RET
;.....
;
;
TRANS:	LDAX	D		;transfer from cmd line to fcb..
	INX	D		;..up to number of chars specified..
	CPI	CR		;..by c-reg. keep scanning field..
	RZ			;..without transfer until a delimiting..
	CPI	'.'		;..field char such as '.', blank, or..
	RZ			;..c/r (for end of cmd line).
	CPI	' '
	RZ
	DCR	C
	JM	TRANS		;once c-reg is less than zero, keep..
	MOV	M,A		;..reading cmd line but do not..
	INX	H		;..transfer to fcb.
	JMP	TRANS
;...
;
;
SCANL:	MVI	B,8		;scan file name addressed by hl.
;
TSTNAM:	MOV	A,M
	CPI	'*'		;if '*' found, fill in rest of field..
	JZ	FILL1		;..with '?' for ambiguous name.
	INX	H
	DCR	B
	JNZ	TSTNAM
	JMP	TSTTYP
;...
;
;
FILL1:	CALL	FILL
;
TSTTYP:	MVI	B,3		;scan and fill type field for name..
;
TSTTYPL:MOV	A,M		;..specified above.
	CPI	'*'
	JZ	FILL2
	INX	H
	DCR	B
	JNZ	TSTTYPL
	RET
;.....
;
;
FILL2:	CALL	FILL
	RET
;.....
;
;
FILL:	MVI	M,'?'		;routine transfers '?'.
	INX	H
	DCR	B
	JNZ	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 DIRLIST2 instead of JMP DIRLIST3 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.
;
DIRLIST:
	CALL	GETDISK	
	ADI	'A'		;change to ascii
	STA	DRNAME		;show for drive name
	STA	ACTDRV		;show for space remaining on drive
;
DIRLIST1:
	JMP	DIRLIST3
;
DIRLIST2:
	MVI	C,RESET		;13 reset disk system (resetdk)
	CALL	BDOS
;
;
; Directory list routine
;
DIRLIST3:
	LXI	D,CMDBUF	;put command line in fcb..
	LXI	H,FCB		;..addressed by hl-reg..
	CALL	CMDLINE		;..and then...
	LXI	H,FCB4
	CALL	INITFCB
	LDA	FCB2		;get drive number
	STA	FCB4
	LDA	FCB2+1
	CPI	' '		;if a space (blank) get all names
	PUSH	PSW
	CZ	QSTMARK
	POP	PSW
	CNZ	MOVNAME		;else move name into fcb
	CALL	DRIVEL
	MVI	C,SETDMA
	LXI	D,TBUF
	CALL	BDOS
	LDA	NOOFCOL		;number of columns into reg-a
	STA	NAMECT		;crlf after "noofcol" number of columns
	LXI	D,FCB4
	MVI	C,SRCHF		;do first search
	CALL	BDOS
	INR	A		;ffh --> 0 if no file(s) found
	JNZ	DIRLOOP
	CALL	ILPRT
	DB	'++ FILE NOT FOUND ++',0
	JMP	STORAGE		;still show storage on default drive
;
DIRLOOP:CALL	GETADD
	INX	H		;point to first letter of filename
	LXI	D,PRTNAME
	LXI	B,8
	CALL	MOVER
	INX	D
	LXI	B,3
	CALL	MOVER
	CALL	ILPRT
;
PRTNAME:DB	'        ','.','   ',0   ; 8 spaces, period, 3 spaces
;
NEXTSR:	LXI	D,FCB4
	MVI	C,SRCHN		;do next search
	CALL	BDOS
	INR	A		;if 0ffh --> 0 then..
	JZ	STORAGE		;..directory-read finished.
	PUSH	PSW
	PUSH	D
	PUSH	H
	LDA	NAMECT
	DCR	A
	STA	NAMECT		;name count updated
	ORA	A
	CZ	CRLF		;terminate line of file names
	JNZ	FENCE
	LDA	NOOFCOL		;restart columns-per-line count
	STA	NAMECT
	JMP	NOFENCE		;fence not needed
;
FENCE:	CALL	ILPRT
	DB	' : ',0		;fence if not at end of line or..
;				;..LAST FILENAME
NOFENCE:POP	H
	POP	D
	POP	PSW
	JMP	DIRLOOP
;.....
;
;
; Determine storage remaining on default drive
;
STORAGE:CALL	CKCPM3
	MVI	C,DSKPAR	;current disk parameter block
	CALL	BDOS
	INX	H
	INX	H
	MOV	A,M		;get block shift factor
	STA	BSHIFTF
	INX	H		;bump to block mask
	MOV	A,M		;get it
	STA	BMASK
	INX	H
	INX	H
	MOV	E,M		;get max block number
	INX	H
	MOV	D,M
	XCHG
	SHLD	BMAX		;put it away
	MVI	C,DSKALL	;address of cp/m allocation vector
	CALL	BDOS
	XCHG			;get its length
	LHLD	BMAX
	INX	H
	LXI	B,0		;initialize block count to zero
;
GSPBYT:	PUSH	D		;save allocation address
	LDAX	D
	MVI	E,8		;set to process 8 blocks
;
GSPLUP:	RAL			;test bit
	JC	NOTFRE
	INX	B
;
NOTFRE:	MOV	D,A		;save bits
	DCX	H
	MOV	A,L
	ORA	H
	JZ	ENDALC		;quit if out of blocks
	MOV	A,D		;restore bits
	DCR	E		;count down 8 bits
	JNZ	GSPLUP		;do another bit
	POP	D		;bump to next count..
	INX	D		;..of allocation vector.
	JMP	GSPBYT		;process it
;
ENDALC:	POP	D		;clear alloc vector pointer from stack
	MOV	L,C		;copy block to hl
	MOV	H,B
	LDA	BSHIFTF		;get block shift factor
	SUI	3		;convert from records to thousands (k)
	JZ	PRTFREE		;skip shifts if 1k blocks
;
FREKLP:	DAD	H		;multiply blocks by k per block
	DCR	A
	JNZ	FREKLP
;
PRTFREE:CALL	DECOUT		;(# of free k bytes now in 'HL')
	LXI	D,FREEMSG
	JMP	PRTMSG
;.....
;
;
; Subroutines for 'DIRLIST' section
;
QSTMARK:MVI	A,'?'		;if blank in fcb, put in 11 ?'S.
	MVI	B,11
	LXI	H,FCB4+1
;
QSTLP:	MOV	M,A
	INX	H
	DCR	B
	JNZ	QSTLP
	RET
;.....
;
;
MOVNAME:LXI	H,FCB2+1
	LXI	D,FCB4+1
	LXI	B,11
	CALL	MOVER
	RET
;.....
;
;
GETADD:	DCR	A		;un-do the inr above
	ADD	A		;times 32
	ADD	A
	ADD	A
	ADD	A
	ADD	A
	ADI	TBUF		;add buffer offset
	MOV	L,A
	MVI	H,0
	RET
;.....
;
;
DRIVEL:	LDA	FCB4		;if no drive, use
	ORA	A		;default drive in drname.
	JZ	PRNTHD
	PUSH	PSW
	DCR	A
	MOV	E,A
	MVI	C,SELDSK
	CALL	BDOS
	POP	PSW
	ADI	40H		;make 1=a, 2=b, etc., and..
	STA	DRNAME		;..overwrite default stored below.
	STA	ACTDRV
;
PRNTHD:	CALL	ILPRT
	DB	'Drive '
;
DRNAME:	DB	' :',CR,LF,0
	RET
;.....
;
;
; Initialized storage
;
FREEMSG:DB	'k bytes free on drive '
ACTDRV:	DB	' :',CR,LF,'$'
;
;
; Uninitialized storage
;
BMAX:	DS	2		;highest block number on drive
BMASK:	DS	1		;rec/blk - 1
BSHIFTF:DS	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	PSW
	PUSH	H
	PUSH	B
	PUSH	D		;'DE' registers must be pushed last
;
INBUFA:	CALL	CLEARBUF	;clear the buffer area
	POP	D		;get address of buffer on retries
	PUSH	D		;restore stack
	XRA	A
	INX	D		;address count field
	STAX	D		;initialize with a zero in count byte
	INX	D
	XCHG			;address first buffer byte with 'HL'
;
INBUFB:	CALL	KEYIN		;(waits for char)
	CALL	UCASE		;convert to upper case if needed
	CPI	CR		;is it <return> (enter command)?
	JZ	INBUFR		;if so, then return.
	CPI	08H		;ctl-h backspaces over deleted character
	JZ	DELETE
	CPI	7FH		;is it a delete?
	JZ	DELETE
	CPI	'U'-40H		;is it a ctl-u?
	JZ	INBUFO		;output #, cr, lf, and start over
	CPI	'R'-40H		;ctl-r retypes line
	JZ	RETYPE
	CPI	EXITCHR		;exit character
	JZ	INBUFC
	CPI	' '		;no control characters other..
	JC	INBUFB		;..than above allowed.
;
INBUFC:	MOV	B,A		;save inputted character
	XCHG			;save 'HL' in 'DE'
	POP	H		;get address of buffer in 'HL'
	PUSH	H		;restore stack
	INX	H		;address count byte
	INR	M		;increase count byte
	DCX	H		;address maximum
	MOV	A,M		;put maximum in 'A'
	INX	H		;address count
	CMP	M		;compare count to maximum
	JC	ALERTL		;if maximum, ring bell and wait for cr.
	XCHG			;restore buffer pointer to 'HL'
	MOV	M,B		;put inputted character in buffer
	MOV	A,B		;output it
	CPI	EXITCHR		;exit character?
	JZ	INBUFR		;if yes, all done
	CALL	TYPE
	INX	H		;bump pointer
	JMP	INBUFB		;get next character
;...
;
;
DELETE:	XCHG			;save buffer pointer in 'DE'
	POP	H		;address beginning of buffer
	PUSH	H		;restore stack
	INX	H		;address count field
	MOV	B,A		;save delete char, 7fh or 08h.
	MOV	A,M
	SUI	1		;decrease count
	MOV	M,A
	JC	NODEL		;don'T DELETE PAST BEGINING OF BUFFER
	XCHG			;restore buffer pointer to 'HL'
	DCX	H		;point to last byte inputted
	MOV	A,B		;get back 7fh or 08h
	MOV	B,M		;get character being deleted
	MVI	M,' '		;restore blank
	CPI	8
	JZ	BKSPC
	CPI	7FH
	JZ	BKSPC0
	JMP	INBUFB		;get next character
;.....
;
;
NODEL:	INR	M		;don'T LEAVE COUNT NEGATIVE
	XCHG			;restore pointer to 'HL'
	JMP	INBUFB
;.....
;
;
BKSPC0:	MVI	A,08H
;
BKSPC:	CALL	TYPE		;true erase if 08h
	MVI	A,' '
	CALL	TYPE
	MVI	A,8
	CALL	TYPE
	JMP	INBUFB
;.....
;
;
INBUFO:	MVI	A,'#'		;announces the line has been removed
	CALL	TYPE
	CALL	CRLF
	JMP	INBUFA
;.....
;
;
RETYPE:	POP	D
	PUSH	D
	INX	D		;point to current number..
	LDAX	D		;..of characters.
	MOV	B,A
	MVI	A,'#'
	CALL	TYPE
	CALL	CRLF
	MOV	A,B		;test if zero input
	ORA	A
	JZ	INBUFB
;...
;
;
CTLRLP:	INX	D
	LDAX	D
	CALL	TYPE
	DCR	B
	JNZ	CTLRLP
	JMP	INBUFB
;.....
;
;	
ALERTL:	MVI	A,BELL		;alarm for full buffer
	CALL	TYPE
	DCR	M
	XCHG
	JMP	INBUFB
;.....
;
;
PCRLF:	CALL	CRLF
	JMP	INBUFB
;.....
;
;
INBUFR:	CALL	CRLF		;1st new line after a command character
	POP	D
	POP	B
	POP	H
	POP	PSW
	RET
;.....
;
;
CLEARBUF:
	POP	D		;accounts for call
	POP	H		;address buffer in 'HL'
	PUSH	H		;restore..
	PUSH	D		;..stack
	MOV	B,M		;save maximum in 'B'
	INX	H		;point to first..
	INX	H		;..buffer byte.
	MVI	A,' '
;
CLEARL:	MOV	M,A
	INX	H
	DCR	B
	JNZ	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.
;
INLNCOMP:
	XTHL			;point 'HL' to 1st char.
	PUSH	D
;
ILCOMPL:MOV	A,M		;'HL' points to in-line string.
	ORA	A		;end of string if zero.
	JZ	SAME
	LDAX	D
	CMP	M
	JNZ	NOTSAME
	INX	H
	INX	D
	JMP	ILCOMPL
;...
;
;
NOTSAME:XRA	A		;if not same, finish thru..
;
NSLP:	INX	H		;..string so return will..
	CMP	M		;..go to instruction after..
	JNZ	NSLP		;..string and not remainder of string.
	STC
;
SAME:	POP	D
	INX	H		;avoids a nop instruction..
	XTHL			;..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.
		
MFNAME:	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,SETDMA
	LXI	D,TBUF
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	XRA	A
	STA	FCBEXT
	LDA	MFFLG1
	ORA	A
	JNZ	MFNAME1
	MVI	A,1
	STA	MFFLG1
	LXI	H,FCB
	LXI	D,MFNAME5
	LXI	B,12
	CALL	MOVER
	LDA	FCB
	STA	MFNAME6		;save disk in current fcb
	LXI	H,MFNAME5
	LXI	D,FCB
	LXI	B,12
	CALL	MOVER
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,SRCHF
	LXI	D,FCB
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	JMP	MFNAME2
;...
;
;
MFNAME1:LXI	H,MFNAME6
	LXI	D,FCB
	LXI	B,12
	CALL	MOVER
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,SRCHF
	LXI	D,FCB
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	LXI	H,MFNAME5
	LXI	D,FCB
	LXI	B,12
	CALL	MOVER
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,SRCHN
	LXI	D,FCB
	CALL	BDOS
	POP	H
	POP	D
	POP	B
;
MFNAME2:INR	A
	STC
	JNZ	MFNAME3
	STA	MFFLG1
	RET
;.....
;
;	
MFNAME3:DCR	A
	ANI	3
	ADD	A
	ADD	A
	ADD	A
	ADD	A
	ADD	A
	ADI	81H
	MOV	L,A
	MVI	H,0
	PUSH	H		;save name pointer
	LXI	D,MFNAME6+1
	LXI	B,11
	CALL	MOVER
	POP	H
	LXI	D,FCB+1
	LXI	B,11
	CALL	MOVER
	XRA	A
	STA	FCBEXT
	STA	FCBRNO
	RET
;.....
;
;
MOVER:	MVI	A,2
	INR	A
	JPE	MFNAME4
	DB	0EDH,0B0H	;z-80 'LDIR' instruction
	RET
;.....
;
;	
MFNAME4:MOV	A,M		;used if an 8080 cpu is active
	STAX	D
	INX	H
	INX	D
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	MFNAME4
	RET
;.....
;
;		(END OF MULTI-FILE ACCESS ROUTINE)	
;=======================================================================
;		 CALCULATE FILE TRANSFER TIME
;
;
; Shows the time to transfer a file at various baud rates.  (110-19200)
;
SENDTIME:
	CALL	ILPRT	      ;print following message:
	DB	'File open: ',0
	LHLD	RCNT		;get record count.
	CALL	DECOUT		;print decimal number of records
	CALL	ILPRT
	DB	' (',0
	CALL	DHXOUT		;now print size in hex.
	CALL	ILPRT
	DB	'H) records',CR,LF
	DB	'Send time: ',0
	LDA	MSPEED		;get the speed indicator
	MVI	D,0
	MOV	E,A		;set up for table access
	LXI	H,BTABLE	;point to baud factor table
	DAD	D		;index to proper factor
	DAD	D		;factor in de
	MOV	E,M
	INX	H
	MOV	D,M
	LHLD	RCNT		;get # of records
	CALL	DVHLDE		;divide 'HL' by value in de (records/min)
	PUSH	H
	MOV	L,C
	MOV	H,B
	CALL	DECOUT		;print the minutes portion
	CALL	ILPRT
	DB	' mins, ',0
	LXI	H,RECDBL	;point to divisors for seconds
	LXI	D,0		;   calculation
	LDA	MSPEED		;get index for baud rate
	MOV	E,A
	DAD	D		;index into table
	MOV	A,M		;get multiplier
	POP	H		;get remainder
	CALL	MULHLA		;multiply the 'HL' x 'A'
	CALL	SHFTHL
	CALL	SHFTHL
	CALL	SHFTHL
	CALL	SHFTHL	
	MVI	H,0
	CALL	DECOUT		;print the seconds portion
	CALL	ILPRT
	DB	' secs at ',0
	CALL	PRTBAUD
	CALL	ILPRTQ
	DB	'To cancel: use CTL-X',CR,LF,0
	RET
;
BTABLE:	DW	5,13,20,24,30,48,96,192,320,480,0 ;records/min for..
RECDBL:	DB	192,74,48,40,32,20,10,5,3,2,0	;110-19200 baud
;.....
;
;
; Shows baud rates set for 'time to send' file transfer
;
PRTBAUD:LXI	H,BAUDSPD
	MVI	D,0
	LDA	MSPEED		;get baud rate code
	MOV	E,A		;x1
	ADD	A		;x2
	ADD	A		;x4
	ADD	E		;x5
	ADD	E
	MOV	E,A
	DAD	D		;point to correct rate
	XCHG
	MVI	C,PRINT
	CALL	BDOS
	CALL	ILPRT
	DB	' baud',CR,LF,0
	RET
;.....
;
;
BAUDSPD:DB	'110$',0,0,'300$',0,0,'450$',0,0,'600$',0,0,'710$',0,0
	DB	'1200$',0,'2400$',0,'4800$',0,'9600$',0,'19200$'
;.....
;
;	
;---->	DVHLDE: Divides 'HL' by value in 'DE',
;	Upon exit: 'BC'=quotient,'L'=remainder
;
DVHLDE:	PUSH	D		;save divisor
	MOV	A,E
	CMA			;negate divisor
	MOV	E,A
	MOV	A,D
	CMA
	MOV	D,A
	INX	D		;'DE' is now two'S COMPLEMENTED
	LXI	B,0		;init quotient
;
DIVL1:	DAD	D		;subtract divisor from dividend
	INX	B		;bump quotient
	JC	DIVL1		;loop till sign changes
	DCX	B		;adjust quotient
	POP	D		;retrieve divisor
	DAD	D		;adjust remainder
	RET
;.....
;
;
;---->	MULHLA:  Multiply the value in 'HL' by the value in 'A'
;		 Return with answer in 'HL'
;
MULHLA:	XCHG			;multiplicand to 'DE'
	LXI	H,0		;init product
	INR	A		;adjust multiplier for zero test
;
MULLP:	DCR	A
	RZ
	DAD	D
	JMP	MULLP
;.....
;
;
; Shift 'HL' register pair one bit to the right
;
SHFTHL:	MOV	A,L
	RAR
	MOV	L,A
	ORA	A		;clear the carry
	MOV	A,H
	RAR
	MOV	H,A
	RNC
	MVI	A,128
	ORA	L
	MOV	L,A
	RET
;.....
;
;		
;	     (END OF FILE TRANSFER TIME ROUTINE)
;=======================================================================
;			 CRC SUBROUTINES
;
;
; Check 'CRC' bytes of record just received
;
CRCCHK:	PUSH	H
	LHLD	CRCVAL
	MOV	A,H
	ORA	L
	POP	H
	RZ
	MVI	A,0FFH
	RET
;.....
;
;
; Generate the CRC tables for fast calculations
;
CRCGEN:	LXI	H,CRCTBL	;address at start of 'CRC' lookup table
	MVI	C,0
;
CRCGEN1:XCHG			;store table location into 'DE'
	LXI	H,0		;clear 'HL' pair
	MOV	A,C
	PUSH	B
	MVI	B,8
	XRA	H
	MOV	H,A
;
CRCGEN2:DAD	H		;index into the table
	JNC	CRCGEN3
	MVI	A,16		;using x^ 16 + x^12 + x^5 + 1 algorithm
	XRA	H
	MOV	H,A
	MVI	A,32+1
	XRA	L
	MOV	L,A
;
CRCGEN3:DCR	B
	JNZ	CRCGEN2		;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	B		;finished borrowing the b reg.
	XCHG			;address back in 'HL', 'CRC' in 'DE'
	MOV	M,D		;store 1st part of 'CRC' value
	INR	H		;move up 256 bytes
	MOV	M,E		;store 2nd part of 'CRC' value
	DCR	H		;move back 256 bytes
	INX	H		;increment to next location
	INR	C		;done when 'C' reg. turns zero again
	JNZ	CRCGEN1		;now go do the next location
	RET
;.....
;
;
; Update the CRC value from a character in the 'A' register
;
CRCUPD:	PUSH	PSW		;save all registers just in case
	PUSH	B
	PUSH	D
	PUSH	H
	LHLD	CRCVAL		;get current value
	XCHG			;put in 'DE' for now
	MVI	B,0
	XRA	D
	MOV	C,A		;now have the character in 'BC' pair
	LXI	H,CRCTBL	;start of 'CRC' lookup-table
	DAD	B		;index into the 'CRC' table	
	MOV	A,M		;get the value from the table
	XRA	E
	MOV	D,A
	INR	H		;move 256 bytes for 2nd table location
	MOV	E,M		;put value there into 'E' register
	XCHG			;put 'DE' into 'HL'
	SHLD	CRCVAL		;updated 'CRC' value with this character
	POP	H		;restore all registers
	POP	D
	POP	B
	POP	PSW
	RET
;.....
;
;==================== END OF CRC SUBROUTINE ============================
;
;
;=========================START OF MENU ================================
;
;
MENU0:	LDA	NFILFLG
	ORA	A
	JZ	MENU		;exit if not saving memory for disk file
	CALL	ILPRT		;else print message
	DB	CR,LF,'** File still open, use DEL, DIR, WRT, E, L '
	DB	'or T ** ',CR,LF,BELL,0
	JMP	MENU1
;
MENU:	XRA	A
	STA	ABORTFLG	;null the flag
;
MENU1:	LXI	H,RESTSN	;restore record numbers..
	LXI	D,RECDNOB	;..for new file transfer.
	MVI	B,RECDNOE-RECDNOB
	CALL	MOVE
	LXI	H,RESTROPT	;restore option table
	LXI	D,OPTBL
	MVI	B,OPTBE-OPTBL
	CALL	MOVE
	XRA	A
	STA	FSTFLG		;reset multi-file trans
	STA	MFFLG1		;reset mfaccess routine..
	JMP	XPRT
;.....
;
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
;		       MENU OF COMMANDS
;
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
;
MENU2:	CALL	CLRTST
	CALL	ILPRT
	DB	'                  Single Letter Commands',CR,LF,LF
	DB	' ?  - Display current settings',CR,LF
	DB	' M  - Display the menu',CR,LF
	DB	' E  - Terminal mode with echo',CR,LF
	DB	' L  - Terminal mode with local echo',CR,LF
	DB	' T  - Terminal mode',CR,LF
	DB	'         For copying text to disk use T (E or L) '
	DB	'FILENAME.TYP',CR,LF
	DB	'         Start or Stop toggles described on subsequent'
	DB	' screen.',CR,LF
	DB	' R  - Receive CP/M file using Christensen Protocol'
	DB	CR,LF
	DB	' S  - Send CP/M file using Christensen Protocol',CR,LF 
	DB	'         COMMAND: R (or S) FILENAME.TYP',CR,LF
	DB	'         R and S can use the following subcommands:'
	DB	CR,LF
	DB	'            B  - Bulk transfer using wildcards '
	DB	'(e.g., *.*)',CR,LF
	DB	'            D  - Disconnect when done'
	DB	CR,LF
	DB	'            Q  - Quiet mode (no messages to console)'
	DB	CR,LF
	DB	'            T  - Return to terminal mode after '
	DB	'transfer',CR,LF
	DB	'            V  - View bytes transferred on console'
	DB	CR,LF
	DB	'            X  - When done, disconnect, go to CP/M'
	DB	CR,LF,LF
	DB	'      The single letter commands may also be used on '
	DB	'the',CR,LF
	DB	'      command line when the program is initially '
	DB	'executed.',CR,LF,LF,0
;
THREELTR:
	CALL	JMP$NXTSCRN
	CALL	ILPRT
	DB	'                Three Letter Commands',CR,LF,LF
	DB	'CPM - Exit from this program to CP/M',CR,LF
	DB	'DIR - List directory and space free (may specify '
	DB	'drive)',CR,LF
	DB	'ERA - Erase file (may specify drive)',CR,LF
	DB	'LOG - Change default drive/user no. (specify '
	DB	'drive/user)',CR,LF
	DB	'      and reset disks.  e.g. LOG A0: or LOG B:  '
	DB	'(user # unchanged)',CR,LF
	DB	'SPD - Set file output speed in terminal mode'
	DB	CR,LF,0
;
	CALL	SORPTST
	JNZ	NOTIME
	CALL	ILPRT
	DB	'TIM - Select Baud rate for "time-to-send" msg.',CR,LF,0
;
NOTIME:	LDA	TOGGLECRC
	ORA	A
	JZ	NOTOGCRC
	CALL	ILPRT
	DB	'TCC - Toggle CRC/Checksum mode on receive',CR,LF,0
;
NOTOGCRC:
	LDA	TOGGLEBK
	ORA	A
	JZ	NOTOGBK
	CALL	ILPRT
	DB	'TBR - Toggle backspace to rubout conversion',CR,LF,0
;
NOTOGBK:LDA	TOGGLELOC
	ORA	A
	JZ	NOTOGLOC
	CALL	ILPRT
	DB	'TLC - Toggle  1) local command immediate',CR,LF
	DB	'              2) local command after ',0
	LDA	EXTCHR
	CALL	SHFTYPE
	DB	CR,LF,0
;
NOTOGLOC:
	LDA	TOGGLELF
	ORA	A
	JZ	NOTOGLF
	CALL	ILPRT
	DB	'TLF - Toggle LF after CR in "L" or "T" mode for '
	DB	'a disk file',CR,LF,0
;
NOTOGLF:LDA	TOGXOFF
	ORA	A
	JZ	NOTOGXOF
	CALL	ILPRT
	DB	'TXO - Toggle XOFF testing in terminal mode '
	DB	'file output',CR,LF,0
;
NOTOGXOF:
	LDA	PMMIMODEM
	ORA	A
	JNZ	NONUM
	LDA	SMARTMODEM
	ORA	A
	JNZ	NOTOGX2
;
NOTOGX1:CALL	ILPRT
	DB	'NUM - List remote systems',CR,LF,0
;
NOTOGX2:LDA	SETUPTST
	ORA	A
	JZ	NONUM
	CALL	ILPRT
	DB	'SET - Set modem baud rate',CR,LF,0
;
NONUM:	CALL	ILPRT
	DB	'BYE - Disconnect, then return to CP/M'
	DB	CR,LF,0
	LDA	PMMIMODEM	;pmmi modem?
	ORA	A
	JNZ	NONUM0		;if yes, display 'CAL'
	LDA	SMARTMODEM
	ORA	A
	JZ	NOPMMI		;exit if neither
;
NONUM0:	CALL	ILPRT
	DB	'CAL - Dial number',CR,LF,0
;
NOPMMI:	CALL	ILPRT
	DB	'DSC - Disconnect from the phone line',CR,LF,LF
	DB	'          The following are terminal text '
	DB	'buffer commands:',CR,LF,LF,0
;
SKIPLF:	CALL	ILPRT
	DB	'DEL - Delete memory buffer and file',CR,LF
	DB	'WRT - Write memory buffer to disk file',CR,LF,LF,0
	CALL	NXTSCRN
	CALL	ILPRT
	DB	'        Local Commands while in Terminal Mode'
	DB	CR,LF,LF,0
	LDA	BRKCHR
	CALL	SHFTYPE
	DB	'  - Send a break tone for 300 ms.',CR,LF,0
	LDA	PMMIMODEM
	ORA	A
	JZ	SKIPLF1
	LDA	CHGBAUD
	CALL	SHFTYPE
	DB	'  - Change baud rate',CR,LF,0
;
SKIPLF1:MVI	A,EXITCHR
	CALL	SHFTYPE
	DB	'  - Exit to command mode',CR,LF,0
	LDA	TRANLOGON
	ORA	A
	JZ	NOTRNLOG
	LDA	LOGCHR
	CALL	SHFTYPE
	DB	'  - Send log-on message',CR,LF,0
;
NOTRNLOG:
	LDA	NOCONNCT
	CALL	SHFTYPE
	DB	'  - Disconnect from the phone line',CR,LF,0
	LDA	LSTTST
	ORA	A
	JZ	NOLIST
	LDA	LSTCHR
	CALL	SHFTYPE
	DB	'  - Toggle printer',CR,LF,0
;
NOLIST: MVI	A,LF
	CALL	TYPE
	LDA	SAVECHR
	CALL	SHFTYPE
	DB	'  - Start copy into buffer',CR,LF,0
	LDA	UNSAVECHR
	CALL	SHFTYPE
	DB	'  - Stop copy into buffer',CR,LF,LF
	DB	'             Start & Stop may be toggled as often as '
	DB	'desired.',CR,LF
	DB	'             A ";" at start of line indicates buffer '
	DB	'is copying.',CR,LF
	DB	'             XOFF automatically used to stop input '
	DB	'when writing',CR,LF
	DB	'                full buffer to disk, XON sent to '
	DB	'resume.',CR,LF,LF,0
	LDA	TRANCHR
	CALL	SHFTYPE
	DB	'  - Transfer ASCII file to remote',CR,LF,LF,0
	LDA	LOCNXTCHR
	ORA	A
	LDA	EXTCHR
	JNZ	REMDFLT
	CALL	SHFTYPE
	DB	'  - Send local control character to remote'
	DB	CR,LF,LF,0
	JMP	CKSPCL
;
REMDFLT:CALL	SHFTYPE
	DB	'  - Next character will be used for local control'
	DB	CR,LF,0
;
CKSPCL:	CALL	JMP$SPCLMENU	;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
	LDA	NFILFLG		;have a file open for text mode copy?
	ORA	A
	JZ	XPRT1		;if not, exit
;
	CALL	GETSPC		;otherwise show remaining space 
	CALL	ILPRT
	DB	' Bytes of buffer free',CR,LF,LF,0
;
;
; Show disk drive and user number, then command line
;
XPRT1:	MVI	C,CURDSK	;current disk function
	CALL	BDOS
	ADI	'A'		;make ascii
	CALL	TYPE
	CALL	GETUSER		;get current user number
	ORA	A
	JZ	XPRT2		;skip if user 0
	MVI	H,0
	MOV	L,A
	CALL	DECOUT		;show user current user area
;
XPRT2:	MVI	A,'>'
	CALL	TYPE
	MVI	A,'>'
	CALL	TYPE
	CALL	ILPRT
	DB	'COMMAND: ',0
;
;
; Get the command line parameters
;
GETCMD:	LXI	D,CMDBUF	;enter command
	CALL	INBUF
	LDA	CMDBUF+2
	CPI	EXITCHR		;exit character
	JZ	XPRT1
	CPI	' '
	JZ	XPRT+3		;skip the extra line feed
	LDA	CMDBUF+3
	CPI	':'		;see if request for new drive/user
	JZ	SETDRV
	LXI	D,CMDBUF+2	;point to command
	CALL	INLNCOMP
	DB	'CPM',0
	JNC	EXIT
	CALL	CRLF		;(1st cr,lf at "inbufr")
	CALL	INLNCOMP
	DB	'LOG',0
	JNC	LOGNEW
	CALL	INLNCOMP
	DB	'DIR',0
	JNC	DIR
	CALL	INLNCOMP
	DB	'ERA',0
	JNC	ERASEF
	CALL	INLNCOMP
	DB	'?',0
	JNC	CURPAR
	CALL	INLNCOMP
	DB	'SPD',0
	JNC	SETSPD
	CALL	INLNCOMP
	DB	'TIM',0
	JNC	SETTIM
	CALL	INLNCOMP
	DB	'TCC',0
	JNC	TOGCRC
	CALL	INLNCOMP
	DB	'TBR',0
	JNC	TOGBKSP
	CALL	INLNCOMP
	DB	'TLC',0
	JNC	TOGLOC
	CALL	INLNCOMP
	DB	'TLF',0
	JNC	TOGLF
	CALL	INLNCOMP
	DB	'TXO',0
	JNC	TOGTXOFF
	LDA	PMMIMODEM
	ORA	A
	JNZ	NONUM1
	LDA	SMARTMODEM
	ORA	A
	JNZ	NONUM1
	CALL	INLNCOMP
	DB	'NUM',0
	JNC	NUMPRN
;
NONUM1:	LDA	SETUPTST
	ORA	A
	JZ	NXTOPT1
	CALL	INLNCOMP
	DB	'SET',0
	JNC	SETUPENT
;
NXTOPT1:CALL	INLNCOMP
	DB	'WRT',0
	JNC	WRTFIL
	CALL	INLNCOMP
	DB	'DEL',0
	JNC	NEWFILE
	CALL	INLNCOMP
	DB	'BYE',0
	JNC	BYEBYE
	CALL	INLNCOMP
	DB	'DSC',0
	JNC	DONETCD
	LDA	PMMIMODEM
	ORA	A
	JNZ	NXTOPT0
	LDA	SMARTMODEM
	ORA	A
	JZ	NXOPT2
;
NXTOPT0:CALL	INLNCOMP	;'DE' set from 1st 'INLNCOMP' call
	DB	'CAL',0
	JC	NXOPT2
	MVI	A,' '		;fool the system
	STA	CMDBUF+4	;..tbuf so that it..
	JMP	DOOPT		;..looks at option for dial
;
NXOPT2:	LDA	CMDBUF+2
	LXI	H,COMPLIST
	CALL	COMPARE		;compares list pointed to by hl..
	JC	NOTVLD		;carry set = no match
;
DOOPT:	CALL	SETFCB		;loads command buffer into fcb
	CALL	PROCOPT		;check out the options
	JMP	RESTART		;go to work
;.....
;
;
NOTVLD:	CALL	NTVLDMSG
	JMP	XPRT
;.....
;
;
NTVLDMSG:
	CALL	ILPRT
	DB	'++ Invalid command ++',CR,LF,BELL,0
	RET
;.....
;
;
BYEBYE:	LDA	PMMIMODEM
	ORA	A
	CNZ	GOODBYE
	LDA	SMARTMODEM
	ORA	A
	CNZ	GOODBYE
	CALL	JMP$GOODBYE	;user'S CUSTOM-AREA GOODBYE ROUTINE
	CALL	ILPRT
	DB	CR,LF,'<<Exit to CP/M>>',CR,LF,0
	JMP	EXIT		;return to cp/m
;.....
;
;
DIR:	MVI	C,CURDSK
	CALL	BDOS
	STA	DISKSAV
	CALL	DIRLIST
	LDA	DISKSAV
	MOV	E,A
	MVI	C,SELDSK
	CALL	BDOS
	JMP	XPRT
;.....
;
;
ERASEF:	LXI	D,CMDBUF	;put cmd line into fcb at 'HL'
	LXI	H,FCB
	CALL	CMDLINE
	CALL	MOVEFCB		;move fcb+16 to fcb
	LDA	FCB+1
	CPI	' '
	JZ	NOTVLD		;go if no file specified
	LXI	D,FCB
	MVI	C,SRCHF
	CALL	BDOS
	INR	A		;0 if file not found
	JNZ	ERAFILE 	;ok, go erase
	CALL	ILPRT
	DB	'++ File not found ++',CR,LF,BELL,0
	JMP	XPRT
;.....
;
;
ERAFILE:LXI	D,FCB
	MVI	C,ERASE
	CALL	BDOS
	CALL	ILPRT
	DB	'File erased',CR,LF,0
	JMP	XPRT
;.....
;
;
LOGNEW:	LDA	NFILFLG		;file open for memory save to disk?
	ORA	A
	JNZ	NORESET		;if yes, do not reset disk drive now
	LDA	CMDBUF+6	;any disk drive specified?
	CPI	' '
	JNZ	LOGNEW1		;if not a blank, exit
	CALL	GETDISK		;if not, use current drive
	ADI	'A'		;to compensate for next line
;
LOGNEW1:SUI	'A'
	CPI	15+1		;for drives 0-15
	JNC	NOTVLD		;if more than 15, display error message
	STA	DISKSAV		;store requested drive
	CALL	GETUSER		;pick up current user number
	MOV	B,A		;save it
	LDA	CMDBUF+7	;get new user number
	CALL	CHRCHK		;check the char.
	CALL	FINDUSER
	LDA	CMDBUF+8		;get 2nd digit
	CALL	CHRCHK		;check the char.
	CALL	FINDUSER+2
;
LOGNEW2:CALL	SAVEUSER	
	MVI	C,RESET
	CALL	BDOS
	LDA	DISKSAV
	MOV	E,A
	MVI	C,SELDSK
	CALL	BDOS
	LDA	SAVUSR
	MOV	E,A
	CALL	SETUSER	
	JMP	XPRT
;.....
;
;
CHRCHK:	CPI	' '
	JZ	CHRCHK1
	CPI	':'		;in case of a: or a1: or a11:  (etc.)
	RNZ
;
CHRCHK1:POP	PSW		;reset the 'CALL' on the stack
	JMP	LOGNEW2
;.....
;
;
FINDUSER:
	MVI	B,0		;zero the 'B' reg. for 1st time through
	CALL	NUMCHK		;if neither, see if a valid number
	MOV	C,A		;save
	MOV	A,B		;get save first digit
	ADD	A		;x2
	ADD	A		;x4
	ADD	A		;x8
	ADD	B		;x9
	ADD	B		;x10
	ADD	C
	MOV	B,A		;save
	RET
;.....
;
;
SAVEUSER:
	MOV	A,B
	CPI	15+1		;user numbers are 0-15
	JNC	NOTVLD
	STA	SAVUSR
	RET
;.....
;	
;
NUMGET:	LXI	D,CMDBUF
	CALL	INBUF
	LDA	CMDBUF+2		;get number
	CPI	' '
	RZ
;
NUMCHK:	SUI	'0'		;remove ascii bias
	CPI	9+1
	RC			;ok if 9 or less
	POP	H		;remove 1st call from the stack
	POP	H		;remove 2nd call from the stack	
	JMP	NOTVLD
;
GETUSER:MVI	E,0FFH		;get current user
;
SETUSER:MVI	C,USER		;set up bdos call
	JMP	BDOS
;.....
;
;
GETDISK:MVI	C,CURDSK	;get current drive
	JMP	BDOS
;.....
;
;
NORESET:CALL	ILPRT
	DB	'++      Terminal mode file open      ++',CR,LF
	DB	'++ Use WRT or DEL before LOG command ++',CR,LF
	DB	CR,LF,BELL,0
	XRA	A
	JMP	XPRT
;.....
;
;
SETSPD:	CALL	ILPRT
	DB	'Delay between chars. (0-9): ',0
;
NOKEYS:	CALL	STAT
	JZ	NOKEYS
	CALL	KEYIN
	CALL	TYPE
	CALL	SAVEA
	SUI	'0'
	CPI	10
	JNC	NOTVLD
	STA	BYTDLY
;
	CALL	ILPRT
	DB	'Delay at end of line (0-9): ',0
;
NOKEYS1:CALL	STAT
	JZ	NOKEYS1
	CALL	KEYIN
	CALL	TYPE
	CALL	SAVEA
	SUI	'0'
	CPI	10
	JNC	NOTVLD
	STA	CRDLY
;
SPDMSG:	CALL	ILPRT
	DB	CR,LF,'Char. delay (terminal file mode) is:  ',0
	LDA	BYTDLY
	MOV	B,A
	MOV	A,B
	PUSH	H
	MOV	L,A
	MVI	H,0
	CALL	DECOUT
	POP	H
	CALL	ILPRT
	DB	'0 ms. per character',CR,LF
	DB	'Line  delay (terminal file mode) is: ',0
	LDA	CRDLY
	MOV	B,A
	PUSH	H
	MOV	L,A
	MVI	H,0
	CALL	DECOUT
	POP	H
	CALL	ILPRT
	DB	'00 ms. per character',CR,LF,0
	JMP	XPRT
;......
;
;
SAVEA:	PUSH	PSW
	CALL	ILPRT
	DB	CR,LF,0
	POP	PSW
	RET
;.....
;
;
SETDRV:	LDA	CMDBUF+2	;get the disk drive
	SUI	'A'		;convert to binary value
	CPI	15+1		;for drives 0-15
	JNC	NOTVLD
	MOV	E,A
	MVI	C,SELDSK	;select requested drive
	CALL	BDOS
	LDA	CMDBUF+5	;get user number, if any
	CPI	' '		;keep current user area?
	JZ	XPRT
	SUI	'0'		;convert to binary value
	CPI	1		;if a '1', could be units or tens
	JNZ	SETDRV1		;if not, numbers stop at 15 so exit
	LDA	CMDBUF+6	;check for a 2nd digit
	CPI	'0'
	JC	SETDRV2		;if less, not a valid number, ignore
	SUI	'0'-10		;leave the '10' in as two digits used
;
SETDRV1:CPI	15+1		;user areas are 0-15
	JNC	NOTVLD
	MOV	E,A
	CALL	SETUSER
	JMP	XPRT		;back to work
;
SETDRV2:MVI	A,1
	JMP	SETDRV1
;.....
;
;
SETTIM:	CALL	SORPTST
	JNZ	NOTVLD
	CALL	ILPRT
	DB	'Use 0-8 to give baud rate for ''S'' mode '
	DB	'time-to-send message,',CR,LF
	DB	'where 0=110, 1=300, 2=450, 3=600, 4=710, 5=1200, '
	DB	'6=2400, ',CR,LF,'7=4800 8=9600 and 9=19200 Baud.'
	DB	CR,LF,LF,'Enter value: ',0
	CALL	NUMGET
	CPI	9+1		;only looking for 0-9 answers
	JNC	NOTVLD
	STA	MSPEED
	CALL	SETTIM1
	JMP	XPRT
;.....
;
;
SETTIM1:CALL	SORPTST
	JNZ	SETTIM2
	CALL	ILPRT
	DB	'Rate for the S mode time-to-send message is set to ',0
	JMP	SETTIM3
;...
;
;
SETTIM2:CALL	ILPRT
	DB	'Modem speed is ',0
;
SETTIM3:JMP	PRTBAUD
;.....
;
;
SORPTST:LDA	SETUPTST	;if setup is 'YES' or pmmimodem is..
	MOV	B,A		;..'YES' or smartmodem is 'YES'..
	LDA	PMMIMODEM	;..return with zero bit not set.
	ORA	B
	RNZ
	LDA	SMARTMODEM
	ORA	B
	RET
;.....
;
;
TOGCRC:	LDA	TOGGLECRC	;allowing crc/checksum toggle?
	ORA	A
	JZ	NOTVLD		;if not, exit
	LDA	CRCDFLT		;get present value and switch it
	CMA
	STA	CRCDFLT
	CALL	TOGCRC1		;show on crt it has been changed
	JMP	XPRT
;.....
;
;
TOGCRC1:CALL	ILPRT
	DB	'Mode: ',0
	LDA	CRCDFLT		;see if set for 'CRC' or 'CHECKSUM'
	ORA	A
	JZ	CHEKMSG
	CALL	ILPRT
	DB	'CRC',CR,LF,0
	RET
;.....
;
;
CHEKMSG:CALL	ILPRT
	DB	'CHECKSUM',CR,LF,0
	RET
;.....
;
;
TOGBKSP:LDA	TOGGLEBK
	ORA	A
	JZ	NOTVLD
	LDA	CONVBKSP
	CMA
	STA	CONVBKSP
	CALL	TOGBKSP1
	JMP	XPRT
;.....
;
;
TOGBKSP1:
	LDA	CONVBKSP
	ORA	A
	JZ	NORUBMSG
	CALL	ILPRT
	DB	'Backspace is rub',CR,LF,0
	RET
;.....
;
;
NORUBMSG:
	CALL	ILPRT
	DB	'Backspace is backspace',CR,LF,0
	RET
;.....
;
;
TOGLOC:	LDA	TOGGLELOC
	ORA	A
	JZ	NOTVLD
	LDA	LOCNXTCHR
	CMA
	STA	LOCNXTCHR
	CALL	TOGLOC1
	JMP	XPRT
;.....
;
;
TOGLOC1:CALL	ILPRT
	DB	'Use ',0
	LDA	LOCNXTCHR
	ORA	A
	LDA	EXTCHR
	JZ	LOCMSG
	CALL	SHFTYPE
	DB	' before local command',CR,LF,0
	RET
;...
;
;
LOCMSG:	CALL	SHFTYPE
	DB	' to send local command to remote',CR,LF,0
	RET
;.....
;
;
TOGLF:	LDA	TOGGLELF
	ORA	A
	JZ	NOTVLD
	LDA	ADDLF
	CMA
	STA	ADDLF
	CALL	TOGLF1
	JMP	XPRT
;.....
;
;
TOGLF1:	CALL	ILPRT
	DB	'LF ',0
	LDA	ADDLF		;adding lf after cr?			
	ORA	A
	JNZ	LFMSG		;if yes, exit
	CALL	ILPRT
	DB	'NOT ',0
;
LFMSG:	CALL	ILPRT
	DB	'sent after CR in "L" or "T" for a disk file',CR,LF,0
	RET
;.....
;
;
TOGTXOFF:
	LDA	TOGXOFF
	ORA	A
	JZ	NOTVLD
	CALL	ILPRT
	DB	'Use XOFF testing? (Y/N): ',0
	CALL	GETANS
	JC	NOCHG3
	STA	XOFFTST
;
NOCHG3:	CALL	XOFFMSG
	CALL	ILPRT
	DB	CR,LF,'Use XON waiting after <CR> (Y/N): ',0
	CALL	GETANS
	JC	NOCHG4
	STA	XONWAIT
;
NOCHG4:	CALL	XONMSG
	LDA	XONWAIT
	ORA	A
	JZ	XPRT
	CMA
	STA	XOFFTST		;don'T ALLOW BOTH
	CALL	ILPRT
	DB	'Therefore ',0
	CALL	XOFFMSG
	JMP	XPRT
;.....
;
;
GETANS:	LXI	D,CMDBUF
	CALL	INBUF
	LDA	CMDBUF+2		;get answer
	CPI	' '
	CMC			;set the carry flag
	RZ
	MOV	B,A
	CPI	'N'
	MVI	A,0
	RZ			;
	MOV	A,B
	CPI	'Y'
	MVI	A,1
	RZ
	POP	PSW		;preserve stack
	JMP	NOTVLD
;.....
;
;
XOFFMSG:CALL	ILPRT
	DB	'XOFF testing ',0
	LDA	XOFFTST
	ORA	A
	JNZ	XOTSTON
	CALL	ILPRT
	DB	'NOT ',0
;
XOTSTON:CALL	ILPRT
	DB	'used',0
;
XONMSG1:CALL	ILPRT
	DB	' in terminal mode file output',CR,LF,0
	RET
;.....
;
;
XONMSG:	CALL	ILPRT
	DB	'XON ',0
	LDA	XONWAIT
	ORA	A
	JNZ	XONMSG2
	CALL	ILPRT
	DB	'NOT ',0
;
XONMSG2:CALL	ILPRT
	DB	'automatically tested after CR',0
	JMP	XONMSG1
;...
;
;
SETUPENT:
	LDA	SETUPTST
	ORA	A
	JZ	NOTVLD
	LXI	D,CMDBUF+1
	CALL	JMP$SETUPR
	LDA	SMARTMODEM
	ORA	A
	JZ	XPRT
	MVI	B,'A'		;send 'AT',cr to smartmodem..
	CALL	SENDCHR		;..to insure its baud rate..
	MVI	B,'T'		;..matches that just selected.
	CALL	SENDCHR
	MVI	B,CR
	CALL	SENDCHR
	JMP	XPRT
;.....
;
;
NEWFILE:LDA	NFILFLG		;file open for disk save?
	ORA	A
	JZ	NOFILOPN	;if not, show "no file open" message
	LDA	FCB3+1		;check that file was requested
	CPI	' '
	JZ	NOFILOPN	;if no file, don'T ERASE
	LXI	D,FCB3		;otherwise erase the old file
	MVI	C,ERASE
	CALL	BDOS
	XRA	A
	STA	NFILFLG		;no file mentioned, reset flags
	STA	SAVEFLG
	LXI	H,FCB3
	CALL	INITFCB
	LXI	H,BUFFER	;reset flags to bottom of ram just..
	SHLD	HLSAVE		;..to insure they are there
	JMP	XPRT
;.....
;
;
WRTFIL:	LDA	NFILFLG		;saving memory for a disk file?
   	ORA	A
	JZ	NOFILOPN	;not saving a file, don'T BOTHER WRITING
	CALL	WRTFIL1		;close the file
	STA	SAVEFLG
	STA	WRFLG
	LXI	H,FCB3
	CALL	INITFCB		;blank out 'FCB' to written file
	LXI	H,BUFFER	;can'T BE ERASED
	SHLD	HLSAVE		;reset to buffer start for next time
	JMP	XPRT
;...
;
;
WRTFIL1:LDA	FCB3+1		;check that file was requested
	CPI	' '
	RZ
	CALL	WRTDSK		;write buffer to disk if not empty
	LXI	D,FCB3		;close the file
	MVI	C,CLOSE
	CALL	BDOS
	XRA	A
	STA	NFILFLG		;file written, reset flags
	RET
;.....
;
;
NOFILOPN:
	CALL	ILPRT
	DB	'++ No File Open ++',CR,LF,BELL,0
	JMP	XPRT
;.....
;
;
; THIS ROUTINE DISPLAYS THE PHONE NUMBERS IN THE LIBRARY
;
NUMPRN:	PUSH	H
	CALL	CLRTST
	CALL	ILPRT
	DB	'           Library of Phone Numbers of Remote Systems'
	DB	0
	MVI	C,13		;number of lines to move
	LXI	H,NUMBLIB	;address of source memory
	LXI	D,BUFFER	;address of target memory
	CALL	NEWLINE		;start with crlf
	STAX	D		;+lf
	INX	D		;and bump it
;
NUMPRN1:INX	H		;skip pmmi dialing letter
	INX	H		;and equal sign
	MVI	B,LIBLEN-2	;number of bytes to move
	CALL	MOVE		;move to buffer
	CALL	SPACES		;2 entries + 3 spaces = 63 characters
	INX	H
	INX	H
	MVI	B,LIBLEN-2
	CALL	MOVE
	CALL	NEWLINE
	DCR	C		;number of lines to print
	JZ	NUMPRN2
	JMP	NUMPRN1
;.....
;
;
NEWLINE:MVI	A,CR		;puts cr-lf at memory pointed by 'DE'
	STAX	D		;store it
	MVI	A,LF		;lf
	INX	D		;bump pointer
	STAX	D		;store lf
	INX	D		;bump pointer
	RET
;.....
;
;
SPACES:	MVI	A,' '		;space
	STAX	D
	INX	D		;1
	STAX	D
	INX	D		;2
	STAX	D
	INX	D		;3
	RET
;.....
;
;
NUMPRN2:MVI	A,'$'
	STAX	D
	MVI	C,PRINT
	LXI	D,BUFFER	;point to table of numbers to print
	CALL	BDOS
	CALL	CRLF
	CALL	CRLF
	POP	H
	JMP	XPRT
;......
;
;
COMPARE:MOV	B,M		;compares a-reg with list..
;
COMPLP:	INX	H		;..addressed by hl. first element..
	CMP	M		;..of list must be number of elements..
	JZ	VALID		;..being compared. returns with..
	DCR	B		;..carry set if a-reg does not..
	JNZ	COMPLP		;.. contain an element in list.
	STC
;
VALID:	RET
;.....
;
;
NXTSCRN:CALL	ILPRT
	DB	'HIT any KEY to CONTINUE',0
;
NOKEY1:	CALL	STAT		;get keyboard status
	JZ	NOKEY1		;keep looping until keypress
	CALL	KEYIN		;gobble up keypress
	CPI	'C'-40H		;control-c to abort?
	JNZ	CLRTST
	POP	H		;clear stack of return address
	CALL	CRLF		;turn up a blank line
	JMP	XPRT
;.....
;
;
CLRTST:	LDA	SCRNTEST
	ORA	A
	JNZ	CLRSCRN
;.....
;
;
LOTSALF:MVI	A,CR
	CALL	TYPE
	MVI	B,12
	MVI	A,LF
;
LFLOOP:	CALL	TYPE
	DCR	B
	JNZ	LFLOOP
	RET
;.....
;
;
CURPAR:	CALL	CLRTST
	CALL	ILPRT
	DB	'                Current Settings',CR,LF,LF,0
	CALL	TOGCRC1
	LDA	LSTTST
	ORA	A
	JZ	NOLIST1
	CALL	LSTMSG
;
NOLIST1:CALL	TOGBKSP1
	CALL	SETTIM1
	CALL	ILPRT
	DB	'Terminal mode file buffer is ',0
	LDA	NFILFLG		;saving memory for a disk file?
	ORA	A
	JNZ	ACTIVE		;if yes, go say "active"
	CALL	ILPRT
	DB	'in',0		;if not, say "inactive"
;
ACTIVE:	CALL	ILPRT
	DB	'active',CR,LF,'Unused portion of buffer is ',0
	CALL	GETSPC
	CALL	ILPRT
	DB	' bytes',CR,LF,0
	CALL	TOGLOC1
	CALL	TOGLF1
	CALL	XOFFMSG
	CALL	XONMSG
	CALL	SPDMSG
	CALL	CRLF
	CALL	CRLF
	CALL	CRLF
	JMP	XPRT
;.....
;
;
GETSPC:	LXI	D,BUFTOP	;top of memory buffer
	LHLD	HLSAVE		;current buffer location
	XCHG
	XRA	A		;clear the carry bit, if set
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A
	CALL	DECOUT		;print the space remaining
	RET
;.....
;
;
;***********************************************************************
;
;	D - A - T - A	 A - R - E - A
;
;***********************************************************************
;
;
COMPLIST: DB	 6, 'S', 'R', 'T', 'E', 'L', 'M'
;.....
;
;
; OPTION TABLE
;
OPTBL:	 EQU	$
ANSWFLG: DB	'A'
BATCHFLG:DB	'B'
DISCFLG: DB	'D'
LOCCHFLG:DB	'L'
ORIGFLG: DB	'O'
QFLG:	 DB	'Q'
RSEEFLG: DB	'R'
SSEEFLG: DB	'S'
TERMFLG: DB	'T'
VSEEFLG: DB	'V'
XITFLG:	 DB	'X'		
EPARITY: DB	'0'	;even parity sub-option (only in s or r mode)
OPARITY: DB	'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:
;
RESTROPT: DB	'A','B','D','L','O','Q','R','S','T','V','X','0','1'
;
;
; The next 13 bytes equal the number of bytes between RECDNOB and
; RECDNOE.
;
RESTSN:  DB	0,0,0,0,0,0
	 DW	BUFFER
	 DB	0,0,0,0,0
;
RECDNOB: EQU	$	;start of table marker
RCVRNO:	 DB	0	;\
RECDNO:	 DB	0,0	; \
ERRCT:	 DB	0	;  \
ERRCDE:	 DB	0	;   \
EOFLG:	 DB	0	;    \	13 bytes between table markers
RECPTR:	 DW	BUFFER	;    /
RECINBF: DB	0	;   /
MAXEXT:	 DB	0	;  /
RCNT:	 DB	0,0	; /
DATAFLG: DB	0	;/
RECDNOE: EQU	$	;end of table marker
;
;
; Additional 16-bit initialized storage
;
CRCVAL:	 DW	0
DIALCT:	 DW	0
HLSAVE:	 DW	BUFFER
HLSAVE1: DW	PBUFF
HLSAVE2: DW	PBUFF
;
;
; Additional general purpose initialized storage
;
ABORTFLG:DB	0
ACKFLG:	 DB	0
CRCFLAG: DB	0	;show in checksum initially
CRFLAG:	 DB	0
CURRENT: DB	52	;pmmi 300 baud speed value (defaults to 300)
DLYFLG:	 DB	0
ECHOFLG: DB	0
EXACFLG: DB	0
FIRSTME: DB	0
FSTFLG:	 DB	0
LISTFLG: DB	0
LOCFLG:	 DB	0
MFFLG1:	 DB	0
MODCTLB: DB	07FH
NFILFLG: DB	0
ONERR:	 DB	0
OPTION:	 DB	0
ORIGSAV: DB	0
RINGBKFL:DB	0
SAVEFLG: DB	0
UARTCTLB:DB	ORIGMOD	;for originate mode
WRFLG:	 DB	0
CMDBUF:	 DB	80H,0	;command buffer (2 bytes + 128 for storage)
;
;
; General purpose unitialized storage area
;
	 DS	128	;storage area for cmdbuf
BGNMS:	 DS	2
DISKNO:	 DS	1
DISKSAV: DS	1
DSTORE:	 DS	1
FILECT:	 DS	1
FTYCNT:	 DS	1
MAXRAM:	 DS	1
NAMECT:	 DS	1
NBSAVE:	 DS	2
OLDUSER: DS	1
SENDFLG: DS	1
SAVUSR:	 DS	1
;
FCB3:	 DS	33
FCB4:	 DS	33
FCBBUF:	 DS	15
MFNAME5: DS	12		;requested name
MFNAME6: DS	12		;current name
	 DS	80		;stack depth
EVENPAGE:EQU	($+255)/256*256 ;sets buffers on even page
;
	 ORG	EVENPAGE
;
STACK:	 EQU	EVENPAGE-2	;store original stack pointer
CRCTBL:	 DS	512		;two tables of 128 bytes each
BUFFER:	 DS	1024*BUFSIZ	;send/receive file buffer
BUFTOP:	 DS	0		;batch-mode filenames buffer
BUFFDSK: DS	128		;buffer for disk save
BUFFPNT: DS	128		;buffer for printer
NAMEBUF: DS	256		;batch-mode filenames buffer
PBUFF:	 DS	0		;printer buffer starts here
;.....
;
;
; BDOS EQUATES
;
RDCON:	EQU	1
WRCON:	EQU	2
LSTOUT:	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
SETDMA:	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

