;MODEM.ASM V2.21, ORIGINALLY BY WARD CHRISTENSEN
; This version will assemble directly for an Intel MDS Series II
;
;	(REVISED 04/19/82)
;
;CP/M - CP/M FILE TRANSFER PROGRAM, AND TERMINAL PROGRAM.
;
;
;--PRESENT CONFIGURATION--
;THIS FILE WILL ASSEMBLE WITHOUT EDITING FOR THE FOLLOWING:
;
; A 2 MHz clock
; External modem
; Ported I/O
; Standart CP/M
; Retry 10 times on block transfer error
; 5 drives (A-E) <-- see MAXDRV equate
; 16K disk buffer size
; Send stop 4 characters early for ascii capture
; Don't go to terminal mode before a file transfer.
;
;--> IF YOU CHANGE ANY CONDITIONALS
;    PLEASE CHANGE THESE COMMENTS, TOO.
;
;--> SEE EQUATES FOR OTHER MODEMS & ADDITIONAL OPTIONS.
;
;* * * * * * * * * * * * * * * * * * * * * * * * *
;*						 *
;*    THIS PROGRAM DOCUMENTED IN "MODEM.DOC"	 *
;*						 *
;* * * * * * * * * * * * * * * * * * * * * * * * *
;* THIS PROGRAM WAS "MODEM.ASM" BUT IS		 *
;* TEMPORARILY NAMED "MODEM2XX.ASM" SO PEOPLE	 *
;* WILL REALIZE IT IS AN ENHANCEMENT OF THE	 *
;* ORIGINAL PROGRAM "MODEM.ASM" ON CP/M USER'S	 *
;* GROUP DISK 25.				 *
;* * * * * * * * * * * * * * * * * * * * * * * * *
;
;MODIFICATIONS/FIXES:
;(IN REVERSE ORDER TO MINIMIZE READING TIME)
;		(4/19/82)
; Added a section of code to reset the receiving
; device (usart, etc) on detection of an error
; (framing, parity, or overrun). The routine used
; is called RESRCVR ("reset receiver") and has
; conditional assemblies for the different types
; of devices. I have an i8251 usart and have
; implemented the particular code for that device,
; but since I no very little about the other
; commonly used devices, you will have to insert
; code for your device.
; This code was necessary for me since when toggling
; between terminal mode and send/receive mode it
; is very likely that the usart will detect an
; overrun error and since until now there was no
; reset to the usart, the one overrun would be
; detected as many times as the program "retried"
; the transfer and would always terminate.
; Again, remember to add your code if you don't
; have an i8251 usart. Put it in a conditional in
; the routine RESRCVR and put the reset code in
; the symbol RSTCODE.
;			Dave Mabry
;
;	04/14/82
;SET DMA TO DEFAULT BUFFER IN CNREC TO CALC PROG
;SIZE CORRECTLY AFTER ASCII CAPTURE AND BEFORE
;FILE XFER. LEAVE ROOM FOR CCP BEYOND ASCBUF BY
;FIXING MEMEND. SEPARATE EQUATES FOR X6850 AND X8251
;FOR BASE I/O ADDRESS TO ALLOW BOTH IN FILE AT
;ONCE FOR MULTIPLE LOCAL MACHINES. MAKE FILE XFERS
;BUFFER SIZE ALTERABLE WITH DBUFSIZ EQUATE. CHECK
;FOR FILE ALREADY EXISTS ON ASCII CAPTURE THEN
;VERIFY BEFORE DELETING IT. PRINT OUT SECTOR #'S
;IN DECIMAL AND HEX ON SEND OR RECEIVE. ADJUST
;SEND AND RECEIVE MSG ALGORITHMS TO LEAVE CURSOR
;AT END OF MSG, NOT FRONT. ALL THE ABOVE CHANGES
;MADE BY ADAPTING THE SAME CODE FROM MODEM7X WHEN
;POSSIBLE TO FACILITATE FUTURE MAINTENANCE.
;
;  THERE IS A TIMEOUT PROBLEM WITH CRC RECEIVE
;WHEN THE SENDING PROGRAM HAS A LARGE BUFFER,
;LIKE 16K. THE PROBLEM IS THAT THE RECEIVING
;PROGRAM SENDS THE INITIAL "C" INSTEAD OF NAK,
;THEN THE SENDING PROGRAM GOES INTO CRC MODE
;AND GOES OFF TO LOAD UP THE 1ST BUFFER OF FILE.
;IF THE SENDING PROGRAM TAKES MORE THAN THE
;COMMON TIMEOUT OF 3 SECONDS TO LOAD UP BEFORE
;SENDING THE 1ST SECTOR, THEN THE RECEIVING
;PROGRAM MAY DECIDE TO SWITCH TO CHECKSUM MODE,
;AND EXPECT A CHECKSUM AFTER THE 1ST SECTOR.
;THE SENDING PROGRAM SENDS A CRC INSTEAD, WHICH
;LOOKS LIKE A BAD CHECKSUM, SO THE RECEIVING
;PROGRAM NAK'S IT. THIS GOES ON UNTIL BOTH
;REACH THEIR ERROR LIMITS, THEN GIVE UP. WHY
;HASN'T THIS PROBLEM SHOWN UP BEFORE? THE COMMON
;XMODEM PROGRAM V4.7 AND UP HAS A 10 SEC TIMEOUT
;ON THE RESPONSE TO THE 1ST "C", NOT 3 SEC,
;WHICH SHOULD BE LONG ENUFF FOR 16K TO LOAD.
;FURTHUR, XMODEM AND MODEM2XX PROGRAMS TRADITIONALLY
;ONLY LOADED 2K BUFFERS, WHICH LOAD UP IN UNDER
;3 SECS. THE ONLY COMMON CASE WOULD BE TRANSFERRING
;A FILE OF 16K OR MORE BETWEEN TWO MODEM7X PROGRAMS,
;WITH CRC, WHERE THE RECEIVER HAD 3 SEC TIMEOUT.
;NOTE THAT IF CRC IS NOT SPECIFIED, THERE IS NO
;PROBLEM. MY VOTE IS TO LENGTHEN THE TIMEOUT
;EVERYWHERE TO 7 SEC OR SO, TO GIVE THE SENDER
;PLENTY OF TIME, BECAUSE THE 16K IS MUCH EASIER
;ON THE DISK, AND A SHADE FASTER TOO. SO THAT HAS
;BEEN DONE HERE. IF YOU GET BIT BY THIS, CHANGE
;DBUFSIZ TO 2K, AND THE 3 SEC LIMITATION SHOULD
;THEN SUFFICE. IF YOU CONTROL BOTH POINTS, WHICH
;SHOULD BE THE ONLY TIME TWO MODEMXX PROGRAMS
;TALK TO EACH OTHER, FIX BOTH ENDS. I REPEAT,
;THERE IS NO PROBLEM TALKING TO XMODEM.
;
;  SINCE ASCII CAPTURE IS NOT IN EFFECT WHEN XFER
;IS GOING ON, EQUATE DBUF AND ASCBUF AT END OF
;PROGRAM. DID NOT CHANGE ASCBUF IN CASE FUTURE
;ENTERPRISING INDIVIDUALS CARE TO PRESERVE ASCII
;CAPTURE STATE OVER AN XFER.  -- Steve Bogolub
;
;	3/31/82
;Added code to convert lower case to upper case and
;trap any illegal characters in filename given for
;ASCII capture.  Added MAXDRV equate so that a non-
;valid drive will be trapped here, rather that bomb
;out of modem program with a BDOS error.  Added code
;to force ASCII capture file closed if it is open
;when terminal mode is exited, or disconnect command
;is given.  Added several new messages.
;			James Underwood (N6CFI)
;
;	3/28/82
;Removed a call to PRINTER in the terminal mode that
;caused double printing of locally typed characters because
;they were printed when sent, and when echoed by the
;remote system.  Also changed all occurances of 'H89' in IF
;statements to 'X8250' so that external 8250 chips are
;properly supported.  Added a place for modem base port
;equate when X8250 and NOT H89 to eliminate an undefined
;symbol error.  Fixed TRS and PMMI equates to eliminate
;doubly defined symbol errors.  Cleaned up file and
;greatly simplified TRS and H89 sections.
;			James Underwood (N6CFI)
;
;       2/21/82
;Cleaned up file, corrected HELP menu, changed
;settings for more "standard" machine, set error
;retries back to 10, made all lines 80 chars or less,
;added check for null filename in capture routines, and
;since the program already suffers from the most advanced
;case of RAMPANT FEATUREITIS known, added VIDEO mode for 
;for use on VIDEO terminals.  (Error msgs will be printed
;on separate lines, but successful "awaiting" or "sending"
;messages will overprint.) The spooling function still doesn't
;work the way I think it should, but it does work the way the
;original author intended it to (I think).
;                             (Dave Hardy)
;
;	1/31/82
;Implemented ascii capture feature and hardcopy
;option similar to that found in CCP. Added code
;for Heath H89/Zenith Z89 and for the 8250 ACE.
;			(Mark McGee)
;
;	10/22/81
;Changed PMMI port equates to be relative to the
;first port equ, thus to change PMMI port values
;one only has to change one address.  Removed the
;PMMI from the IF for INITREQ since PMMI does not
;require port initialization and in fact if it is
;initialized with INITREQ, the line will be lost.
;Changed receive sector routine so that on the
;first time thru when CRC is being used, it only
;waits for 3 seconds to receive the SOH after
;sending the initial 'C'.  If a character is not
;received in 3 seconds, then a NAK is sent and this
;program switches to checksum mode.  This allows
;the CRC MODEM to be used with versions of XMODEM
;that do not support CRC, even when MODEM has
;specified a CRC transmission.	The transmission
;will then take place using checksum instead of
;CRC.
;	Changed the default baud rate for the PMMI
;to an equate.	By changing the 'DEFBAUD EQU', the
;default baud rate for the PMMI can set to any rate.
;Grouped all of the EQU's for the PMMI under one 'IF'
;statement for ease of maintenance and documentation
;purposes.
;	Added equate(TERMNL EQU) to give one the option
;of whether or not to go to terminal mode before a
;file transfer.
;				(John Mahr)
;
;	10/19/81
;FIXED 'SEND #' AND 'AWAITING #' STATEMENTS TO
;PROPERLY DISPLAY THE CURRENT SECTOR # BEING SENT OR
;RECEIVED.  FIXED SEND MSG TO DISPLAY PROPER NUMBER
;OF RECORDS IN THE FILE TO BE SENT (FILES LARGER THAN
;1 EXTENT) USING THE SAME ROUTINES FOUND IN XMODEM43.
;IF NO OPTIONS SELECTED ON INITIAL ENTRY, JUMP TO
;'BADOPT' WITHOUT INITIALIZING MODEM.  FIXED THE EQUATES
;FOR THE DCH ERROR MASKS FOR PARITY, OVERRUN & FRAMING.
;MADE A SEPARATE CONDITIONAL ASSEMBLY FOR DCH & X6850
;UART EQUATES (JUST IN CASE THE X6850 INFO WAS CORRECT)
;AND ELIMINATED THE DCH "INITC1" THRU "INITC4" EQUATES
;SINCE THEY ARE NOT USED BY DCH DURING THE "INITMD"
;CONDITIONAL ASSEMBLY.  (BILL ATEN)
;
;	10/12/81
;ADDED CYCLIC REDUNDANCY CHECK OPTION ON THE FILE
;RECEIVE OPTION.  THIS IN ANOTHER SECONDARY OPTION
;THAT IS SPECIFIED BY SPECIFYING A 'C'.
;	MODEM RC.600 fn.ft
;	MODEM ROC.300 fn.ft, etc.
;		NOTE: CANNOT HAVE MORE THAN 6 SEC OPTIONS.
;WHEN THE FILE RECEIVE SPECIFIES CRC, THE LETTER 'C' IS
;SENT IN PLACE OF THE INITIAL NAK.  THIS SIGNALS THE SENDER
;(XMODEM45+ or MODEM213+) THAT CRC IS IN EFFECT.  THE SENDING
;PROGRAM WILL AUTOMATICALLY SWITCH TO CRC MODE.  THE CRC
;WILL REPLACE THE CHECKSUM METHOD OF CHECKING FOR DATA
;INTEGRITY ON FILE TRANSMISSIONS.  CRC WILL GIVE BETTER
;THAN A 99.99% PROBABILITY THAT THERE ARE NO DATA INTEGRITY
;ERRORS.  ACKNOWLEDGEMENT AND THANKS TO PAUL HANSKNECHT
;WHO DESIGNED AND WROTE CRC120.  IT IS THE CRC120 MACRO
;THAT WAS USED TO IMPLEMENT CRC IN THIS PROGRAM.
;					(JOHN MAHR)
;
;	10/08/81
;ADDED TRS80 MODEL I SUPPORT INCLUDING BAUD RATE 
;SELECT FROM COMMAND LINE.   (MARK C WEHMHOEFER)
;
;	10/02/81
;FIXED RCVFILE BUG IN 10/1 VERSION.  REVISED MOST
;MESSAGES TO UPPER & LOWER CASE.  CHANGED RCVFILE SO
;IT SENDS THE INITIAL NAK AS SOON AS THE FILE IS OPEN,
;WITHOUT WAITING FOR A TIMEOUT FIRST.  (DHH)
;	*NOTE: A FILE TRANSFER WILL FAIL IF THE SEND
;		END IS NOT STARTED BEFORE THE RECEIVE
;		END WHEN THERE IS NO WAIT BEFORE
;		SENDING THE INITIAL NAK OR CRC
;		REQUEST. (10/12/81, JRM)
;
;	10/02/81
;FIXED DUPLICATE EQUATE FOR BASE ADDRESS, REMOVED UNUSED
;EQUATE FOR H8CPM.  (KBP)
;
;	10/01/81
;ADDED ERROR MASKS FOR HAYES MICROMODEM & 6850 ACIA.
;ADDED "X6850" CONDITIONAL.  ADDED "PORTED" CONDITIONAL
;TO ALLOW USE ON MEMORY-MAPPED SYSTEMS SUCH AS THE
;APPLE II.  COLLECTED MODEM PRIMITIVE OPERATIONS TO
;FRONT OF PROGRAM.  MADE RCV ERROR CHECKING ACTIVE FOR
;ALL MODEMS.  (DAV HOLLE)
;
;	9/11/81
;FIXED BUG IN 6/2 MOD.  ADDED BELL TO CERTAIN ERROR MSGS.
;ADDED R & S OPTIONS MSGS.  CHANGED MULTI TO X8251
;CONDITIONAL & MOVED WITH MODEM TYPES. CHANGED SIGNON
;VERSION MESSAGE. (TED SHAPIN)
;
;       06/02/81
;ADDED BELL WHEN TRANSFER IS FINISHED.  SHORTENED LABELS
;TO 6 CHARS SO OTHER ASSEMBLERS WILL WORK.
;ADDED CALL TO 'TERM' FROM BOTH SEND AND RECEIVE. THIS
;LETS YOU CONTROL THE REMOTE SYSTEM BEFORE TRANSMISSION.
;AFTER YOU LOG ON, ETC., AND TYPE "XMODEM R FOO.ASM"
;OR WHATEVER. YOU CAN THEN TYPE CONTROL-E TO PUT THIS PROGRAM
;INTO SEND OR RECEIVE MODE. (TED SHAPIN, ORANGE, CA.)
;
;	05/07/81
;ADDED TRAPS FOR AMBIGUOUS FILE NAME OR NONE AT ALL.
;REARRANGED EQUATES FOR GREATER CLARITY.  CLEANED UP
;FILE.  (KBP)
;
;	05/02/81
;ADDED THE ABILITY TO DISPLAY MODEM STATUS ON A
;FRONT PANEL, IF ONE HAS ONE (SUCH AS AN ITHACA
;INTERSYSTEMS DPS-1).
;	1. FRNTPNL EQU TRUE TURNS IT ON
;	2. PANEL EQU 0FFH (SETS UP PORT ADDRESS
;		OF FRONT PANEL)  (JOHN MAHR)
;
;	05/01/81
;RESTORED HELP DISPLAY.  LOWER CASG CHARS AND TABS
;HAD BEEN TAKEN OUT.  ADDED TYPICAL EXTERNAL PORT
;EQUATES AND INIT VALUES.  REARRANGED ORDER OF
;MODIFICATION/FIXES INFO.  (KBP)
;
;	04/18/81
;ADDED DETECTION OF FRAMING ERRORS, OVERRUN ERRORS,
;PARITY ERRORS (IF PARITY IS USED) FOR THE RECEIVE
;FILE ROUTINE.  THIS FEATURE IS ONLY ACTIVE FOR
;THE PMMI MODEM, SINCE I DO NOT KNOW WHAT THE MODEM
;STATUS BITS ARE FOR IDS AND D.C. HAYES MODEMS.
;IF THERE IS ONE OF THE MENTIONED ERRORS, THE LINE
;WILL BE PURGED FOR THAT BLOCK AND A NAK WILL BE
;SENT TO THE SENDER FOR THAT BLOCK.  A MESSAGE TO
;OPERATOR WILL ALSO BE DISPLAYED.  (BY JOHN MAHR)
;
;	05/27/80
;ELIMINATED CONTROL-X CANCEL OF SEND FEATURE, AT
;SUGGESTION OF WARD CHRISTENSEN. A LINE GLITCH COULD
;CAUSE PREMATURE ABORT WHEN THIS FEATURE WAS ACTIVE.
;ADDED EQUATES FOR FALSE AND TRUE TO MAKE ASSEMBLY
;OPTIONS CLEARER. REMOVED H8 PORT EQUATES (THEY CAN
;BE PUT IN EXTERNAL MODEM EQUATES). (KBP)
;
;	12/06/79
;CORRECTED ERROR IN HELP FILE. SAID T.110, NOW SAYS
;TO.110. BY WARD CHRISTENSEN. CORRECTED RECEIVE FILE
;ROUTINE SO TERMINAL OR ECHO MODE WORKS AFTER FILE
;TRANSFER IN QUIET MODE. MOVED CHECKS FOR "H" AND
;"X" OPTIONS SO MODEM IS NOT REINITIALIZED. (KBP)
;
;	08/06/79
;ADDED EQUATES FOR EXTERNAL MODEM (NOT S-100 PLUG-IN)
;(KBP)
;
;	08/05/79
;ADDED D.C. HAYES MODEM SUPPORT BY JIM BELL  (KBP)
;
;	07/24/79
;MOVE INITIALIZE LOCAL STACK TO BEGINNING OF PROGRAM
;SO DEFAULT STACK IS NOT USED. ADD CONDITIONAL ASSEMBLY
;OPTION TO TERMINAL ROUTINE FOR TIMESHARE SYSTEMS.
;CORRECT ERROR IN LOCAL ABORT ROUTINE (WAS LOOKING FOR
;CONTROL-E - NOW CORRECTLY LOOKS FOR CONTROL-X). ADD
;REGISTER SAVES TO CONOUT, KEYIN AND KEYBOARD STATUS
;ROUTINES, AS SOME CBIOS ROUTINES CLOBBER THEM. (KBP)
;
;	07/01/79
;MODIFIED PROGRAM TO ALLOW FOR NON-STANDARD VERSIONS OF
;CP/M. ALL REFERENCES TO ENTRIES INTO CP/M SHOULD BE MADE
;RELATIVE TO THE VARIABLE SYMBOL CALLED "BASE". FOR EXAMPLE,
;THE EQUATE TO BDOS SHOULD BE BASE+5 INSTEAD OF 5. BASE
;WILL BE SET TO 0 WHEN THE VARIABLE STDCPM IS SET TO TRUE.
;(BOB MATHIAS).
;
;	05/24/79
;FIXED MISSING RETURN INSTRUCTION AT END OF
;INITIALIZATION ROUTINE.  (KBP)
;
;	05/22/79
;ADDED FEATURE TO MAKE RECEIVE FILE ROUTINE SAY
;FILE SUCCESSFULLY OPENED, WHEN IN QUIET MODE.
;MOVED INITIAL 'GOBBLE GARBAGE INPUTS' TO BEFORE
;COMMAND CPI'S SO ALL MODES ARE CLEARED. CHANGED
;INITIAL SEND WAIT TO 80 SECS TO ALLOW MORE TIME
;FOR RECEIVING END TO COME UP. ADDED 'H' AFTER MSG
;THAT SHOWS NUMBER OF SECTORS IN EXTENT ABOUT TO
;BE SENT.  (KBP)
;
;	05/09/79
;ALLOW 'T' AND 'E' SUB-OPTIONS TO GO TO TERMINAL
;OR ECHO MODE AFTER TRANSFERRING A FILE.  (WLC)
;
;	04/26/79
;REWRITTEN BY WARD CHRISTENSEN TO COMBINE
;IMPROVEMENTS TO THE ORIGINAL MADE BY WARD
;AND BY KEITH PETERSEN, W8SDZ, AND SUGGESTIONS
;BY JIM BELL WHICH KEITH IMPLEMENTED.  SEE
;MODEM.DOC FOR ADDITIONAL HISTORICAL
;INFORMATION AND DOCUMENTATION.
;
;	09/23/77
;ORIGINALLY WRITTEN BY WARD CHRISTENSEN
;
;	--------------
;
;NOTE: IF YOU ADD IMPROVEMENTS OR OTHERWISE UPDATE
;THIS PROGRAM, PLEASE MODEM A COPY OF THE NEW FILE
;TO "TECHNICAL CBBS" IN DEARBORN, MICHIGAN - PHONE
;313-846-6127 (110, 300, 450 OR 600 BAUD).  USE THE
;FILENAME MODEM.NEW.	(KBP)
;
;	--------------
; DEFINE EQUATES
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
STDCPM	EQU	TRUE		;TRUE, IS STANDARD CP/M
ALTCPM	EQU	FALSE		;TRUE, IS ALTERNATE CP/M FOR H8 OR TRS80
DBUFSIZ	EQU	16		;BUFFER SIZE IN KBYTES
;
; DEFINE TYPE OF CP/M IN USE
;
	IF	STDCPM
BASE	EQU	0		;CP/M BASE ADDRESS
	ENDIF
;
	IF	ALTCPM
BASE	EQU	4200H		;CP/M BASE ADDRESS FOR ALTERNATE CP/M
	ENDIF
;
; DEFINE MODEM INTERFACE
;
DCH	EQU	FALSE		;TRUE, IS D.C. HAYES MODEM
PMMI	EQU	FALSE		;TRUE, IS PMMI MODEM
H89	EQU	FALSE		;TRUE, IS HEATH H89
				;BE SURE X8250 IS ALSO TRUE
TRS	EQU	FALSE		;TRUE FOR TRS80 MODEL I
				;BE SURE X8251 IS ALSO TRUE
;
X8250	EQU	FALSE		;TRUE, IS EXTERNAL 8250 ACE
X8251	EQU	TRUE		;TRUE, IS EXTERNAL 8251 USART
X6850	EQU	FALSE		;TRUE, IS EXTERNAL 6850 ACIA
;
PORTED	EQU	TRUE		;TRUE FOR IN/OUT PORTS, FALSE FOR LDA/STA
INITREQ EQU	TRUE		;TRUE IF PORT MODEM CONNECTED TO...
;				 ...REQUIRES INITIALIZATION.
MAXDRV	EQU	'E'		;MAX DRIVE ON SYSTEM
;
; DEFINE OTHER SYSTEM PARAMETERS
;
FASTCLK EQU	FALSE		;TRUE FOR 4 MHZ CLOCK, FALSE FOR 2 MHZ
FRNTPNL EQU	FALSE		;TRUE FOR FRONT PANEL DISPLAY
VIDEO	EQU	FALSE		;TRUE FOR USE WITH VIDEO TERMINAL
TERMNL	EQU	FALSE		;TRUE, GO TO TERMINAL MODE BEFORE..
;				 ...FILE TRANSFER.
;
	IF	FRNTPNL
PANEL	EQU	0FFH		;PORT ADDRESS FOR FRONT PANEL
	ENDIF
;
; SOME TIME-SHARE COMPUTERS REQUIRE TERMINALS TO
; HAVE BIT 7 HIGH (MARKING), SO IN THE TERMINAL
; MODE WE FORCE IT TO HIGH IF THE FOLLOWING OPTION
; IS SELECTED:
;
TIMESHR EQU	FALSE		;TRUE TO MAKE BIT 7 HIGH
;*****************************************************************************
	IF	PMMI
; SET DCH=FALSE,H89=FALSE,PMMI=TRUE,X8250=FALSE,X8251=FALSE,
; X6850=FALSE,PORTED=TRUE,INITREQ=FALSE
;
; THESE EQUATES SET ALL OF THE VALUES NECESSARY TO USE A PMMI.
; THE ONLY ONE THAT SHOULD EVER HAVE TO BE CHANGED IS MDCTLP
; WHICH IS THE ADDRESS OF THE FIRST PORT ON THE PMMI.  THIS
; ADDRESS IS SET BY SWITCHES ON THE PMMI CARD.
;
MDCTLP	EQU	0E0H		;PMMI VALUES (BASE ADDR OF I/O PORTS)
MDDATP	EQU	MDCTLP+1	;DATA PORT
BAUDRP	EQU	MDCTLP+2	;BAUD RATE OUTPUT
MDCTL2	EQU	MDCTLP+3	;SECOND CTL PORT
ORIGMD	EQU	1DH		;8 DATA, NO PARITY, ORIG
ANSWMD	EQU	1EH		;8 DATA, NO PARITY, ANSW
;
FRMER	EQU	20H		;FRAMING ERROR MASK
ORUNER	EQU	10H		;OVERRUN ERROR MASK
PARER	EQU	08H		;PARITY ERROR MASK
;
MDSNDB	EQU	1		;BIT TO TEST FOR SEND
MDSNDR	EQU	1		;VALUE WHEN READY
MDRCVB	EQU	2		;BIT TO TEST FOR RECEIVE
MDRCVR	EQU	2		;VALUE WHEN READY
;
DEFBAUD EQU	52		;DEFAULT TO 300 BAUD
				;USE 52 FOR 300 BAUD
				;USE 26 FOR 600 BAUD
				;USE 104 FOR 150 BAUD
				;USE 142 FOR 110 BAUD
	ENDIF			;PMMI
;*****************************************************************************
	IF	DCH
MDCTLP	EQU	82H		;D. C. HAYES VALUES
MDDATP	EQU	80H		;DATA PORT
MDCTL2	EQU	81H		;SECOND CTL PORT
;
; NOTE: BAUD RATE DEFAULTS TO 300 - 1 STOP BIT.
; DO NOT CHANGE NEXT EQUATES:
;
ORIGMD	EQU	86H		;OFF HOOK, 110 BAUD, CAR. ON, ORIG.
ANSWMD	EQU	82H		;OFF HOOK, 110 BAUD, CAR. ON, ANSW.
	ENDIF			;DCH
;*****************************************************************************
;
; FOR APPLE CARDS:  PORTED=FALSE AND X6850=TRUE, AND
; MDDATP IS 1 MORE THAN MDCTLP. FOR SSM AIO, APPLE COM
; CARD, OR CCS 7710 USE MDCTLP=0E0ACH (FOR SLOT 2), OR
; USE 0E0AEH FOR MICROMODEM II. FOR DIFFERENT SLOTS,
; ADD (SLOT-2)*10H TO THE ABOVE ADDRESSES.
;
;-->IF USING EXTERNAL MODEM WITH 8250, 8251 OR 6850
;-->CHIP, CHANGE THESE EQUATES TO YOUR SYSTEM REQUIREMENTS
;
	IF	X6850
MDCTLP	EQU	0E0AEH		;MODEM CONTROL PORT
MDDATP	EQU	MDCTLP+1	;MODEM DATA PORT
	ENDIF	;X6850
;
	IF	X8251
MDCTLP	EQU	0F7H		;MODEM CONTROL PORT
MDDATP	EQU	MDCTLP-1	;MODEM DATA PORT
	ENDIF	;X8251
;
	IF	X8250 AND NOT H89
MDDATP	EQU	60H	;YOUR MODEM DATA PORT
	ENDIF			;X8250 AND NOT H89
;
;-->END OF EXTERNAL MODEM BASE EQUATES
;
;*****************************************************************************
	IF	H89
;FOR H89 SET THESE TRUE, ALL OTHERS FALSE:
;	H89,X8250,PORTED,STDCPM
;
MDDATP	EQU	0D8H		;HEATH MODEM PORT BASE ADDRESS
	ENDIF			;H89
;*****************************************************************************
	IF	TRS
;FOR TRS SET THESE TRUE, ALL OTHERS FALSE:
;	TRS,X8251,PORTED,ALTCPM
;
MDCTLP	EQU	0EAH		;TRS-80 WITH RS232 VALUES
MDDATP	EQU	0EBH		;DATA PORT
	ENDIF			;H89
;*****************************************************************************
	IF	X8250
MDCTL1	EQU	MDDATP+1	;INTERRUPT CONTROL
MDCTL3	EQU	MDDATP+3	;WORD CHARACTERISTICS
MDCTL4	EQU	MDDATP+4	;LINE CONTROL
MDCTL5	EQU	MDDATP+5	;LINE STATUS
	ENDIF			;X8250
;*****************************************************************************
;
; THESE EQUATES SPECIFY THE INITIALIZATION SEQUENCE
; AND STATUS FLAGS USED BY YOUR TYPE OF MODEM.
;
;
	IF	X8251
INITC1	EQU	00H		;1ST INIT CHAR TO 8251 CTL PORT
INITC2	EQU	00H		;2ND
INITC3	EQU	00H		;3RD
INITC4	EQU	37H		;4TH
;
RSTCODE	EQU	37H		; Reset error, set DTR,
				;  enable receiver and
				;  transmitter
;
FRMER	EQU	20H		;FRAMING ERR MASK
ORUNER	EQU	10H		;OVERRUN ERR MASK
PARER	EQU	08H		;PARITY ERR MASK
;
MDSNDB	EQU	1		;BIT TO TEST FOR SEND
MDSNDR	EQU	1		;VALUE WHEN READY
MDRCVB	EQU	2		;BIT TO TEST FOR RECEIVE
MDRCVR	EQU	2		;VALUE WHEN READY
	ENDIF			;X8251
;*****************************************************************************
	IF	X8250
INITC1	EQU	83H		;ACCESS DIVISOR LATCHES
INITC2	EQU	03H		;SET DTR AND RTS
INITC3	EQU	80H		;LOW BAUD DIVISOR (300 BAUD)
INITC4	EQU	01H		;HIGH BAUD DIVISOR (300 BAUD)
INITC5	EQU	03H		;8 BITS,1 STOP, NO PARITY
INITC6	EQU	00H		;DISABLE ALL INTERRUPTS
;
FRMER	EQU	08H		;FRAMING ERR MASK
ORUNER	EQU	02H		;OVERRUN ERR MASK
PARER	EQU	04H		;PARITY ERR MASK
;
MDSNDB	EQU	20H		;BIT TO TEST FOR SEND
MDSNDR	EQU	20H		;VALUE WHEN READY
MDRCVB	EQU	01H		;BIT TO TEST FOR RECEIVE
MDRCVR	EQU	01H		;VALUE WHEN READY
	ENDIF			;X8250
******************************************************************************
	IF	DCH
PARER	EQU	04H		;PARITY ERR MASK
FRMER	EQU	08H		;FRAMING ERR MASK
ORUNER	EQU	10H		;OVERRUN ERR MASK
;
MDSNDB	EQU	2		;BIT TO TEST FOR SEND
MDSNDR	EQU	2		;VALUE WHEN READY
MDRCVB	EQU	1		;BIT TO TEST FOR RECEIVE
MDRCVR	EQU	1		;VALUE WHEN READY
	ENDIF			;DCH
;*****************************************************************************
	IF	X6850
INITC1	EQU	3		;1ST INIT CHAR TO 6850 CTL PORT
INITC2	EQU	15H		;2ND, 8 DATA + 1 STOP + NO PARITY,
				; 16X CLOCK. USE 16H FOR SAME WITH 64X
				; CLOCK TO SWITCH FROM 1200 TO 300 BAUD,
				; FOR EXAMPLE.
INITC3	EQU	INITC1		;3RD (ONLY 2 NEEDED, REUSE 1 & 2)
INITC4	EQU	INITC2		;4TH
;
FRMER	EQU	10H		;FRAMING ERR MASK
ORUNER	EQU	20H		;OVERRUN ERR MASK
PARER	EQU	40H		;PARITY ERR MASK
;
MDSNDB	EQU	2		;BIT TO TEST FOR SEND
MDSNDR	EQU	2		;VALUE WHEN READY
MDRCVB	EQU	1		;BIT TO TEST FOR RECEIVE
MDRCVR	EQU	1		;VALUE WHEN READY
	ENDIF			;X6850
;*****************************************************************************
;
; DEFINE SOME OTHER THINGS (NORMALLY NOT CHANGED)
;
ERRLIM	EQU	10		;MAX ALLOWABLE ERRORS (10 STANDARD)
STPTIME EQU	4		;SEND STOP THIS MANY CHARS EARLY
CPTRKEY EQU	'Y'-40H 	;CTL-Y TOGGLES ASCII CAPTURE MODE
DISCCHR EQU	'D'-40H 	;CTL-D DISCONNECTS MODEM T/E
EXITCHR EQU	'E'-40H 	;CTL-E EXIT FROM T OR E
PRTCHAR EQU	'P'-40H 	;CTL-P TOGGLES PRINTER OPTION
STRTCHR EQU	'Q'-40H 	;CTL-Q TO MAKE OTHER END RESUME (MOST SYSTEMS
				;WILL ACCEPT ANY CHAR,SO CTL-Q IS TO INCLUDE
				;THE XON/XOFF PROTOCALL)
STOPCHR EQU	'S'-40H 	;CTL-S TO MAKE OTHER END PAUSE
;
; DEFINE ASCII CHARACTERS USED
;
SOH	EQU	1		;START OF HEADER
EOT	EQU	4		;END OF TRANSMISSION
ACK	EQU	6		;ACKNOWLEDGE
NAK	EQU	15H		;NEG ACKNOWLEDGE
CRC	EQU	'C'		;USED TO REQUEST CRC INSTEAD OF CHECKSUM
CAN	EQU	18H		;CANCEL
LF	EQU	10		;LINEFEED
CR	EQU	13		;CARRIAGE RETURN
BELL	EQU	'G'-40H 	;BELLS
;*****************************************************************************
	ORG	BASE+100H
;
;INIT PRIVATE STACK
	LXI	H,0		;HL=0
	DAD	SP		;HL=STACK FROM CP/M
	SHLD	STACK		;..SAVE IT
	LXI	SP,STACK	;SP=MY STACK
	LDA	BASE+0007H	;BDOS PAGE ADDRESS
	SUI	8+1		;ACCOUNT FOR CCP (2K) + 1 PG SLOP
	STA	MEMEND		;  AND SAVE THE RESULT
	CALL	START		;GO PRINT ID
	DB	'MODEM ver 2.21A of 04/19/82'
	DB	CR,LF,'$'
;
; MODEM I/O PRIMITIVES
;
; COLLECTED HERE FOR EASIER PATCHING & MAINTENANCE.
; PORTED I/O ROUTINES HAVE NOP'S TO LEAVE ROOM
; FOR LATER PATCHING TO LDA'S & STA'S IF NECESSARY.
;
	IF	PORTED AND (NOT X8250)
OUTDATA OUT	MDDATP
	RET
	NOP
INDATA	IN	MDDATP
	RET
	NOP
OUTCTL	OUT	MDCTLP
	RET
	NOP
INCTL	IN	MDCTLP
	RET
	NOP
	ENDIF			;PORTED AND (NOT X8250)
;
	IF	PORTED AND X8250
OUTDATA OUT	MDDATP
	RET
	NOP
INDATA	IN	MDDATP
	RET
	NOP
OUTCTL1 OUT	MDCTL1
	RET
	NOP
OUTCTL3 OUT	MDCTL3
	RET
	NOP
OUTCTL4 OUT	MDCTL4
	RET
	NOP
INCTL5	IN	MDCTL5
	RET
	NOP
	ENDIF			;PORTED AND X8250
;
	IF	PORTED AND (DCH OR PMMI)
OUTCT2	OUT	MDCTL2
	RET
	NOP
INCT2	IN	MDCTL2
	RET
	NOP
	ENDIF			;PORTED AND (DCH OR PMMI)
;
	IF	PORTED AND PMMI
OUTBRP	OUT	BAUDRP
	RET
	NOP
	ENDIF			;PORTED AND PMMI
;
	IF	PORTED AND FRNTPNL
OUTPAN	OUT	PANEL
	RET
	NOP
	ENDIF			;PORTED AND FRNTPNL
;
	IF	NOT PORTED
OUTDATA STA	MDDATP
	RET
INDATA	LDA	MDDATP
	RET
OUTCTL	STA	MDCTLP
	RET
INCTL	LDA	MDCTLP
	RET
	ENDIF			;NOT PORTED
;
	IF	(NOT PORTED) AND (DCH OR PMMI)
OUTCT2	STA	MDCTL2
	RET
INCT2	LDA	MDCTL2
	RET
	ENDIF			;(NOT PORTED) AND (DCH OR PMMI)
;
	IF	PMMI AND NOT PORTED
OUTBRP	STA	BAUDRP
	RET
	ENDIF			;PMMI AND NOT PORTED
;
	IF	FRNTPNL AND NOT PORTED
OUTPAN	STA	PANEL
	RET
	ENDIF			;FRNTPNL AND NOT PORTED
;
START	POP	D		;GET ID MESSAGE
	MVI	C,PRINT
	CALL	BDOS		;PRINT ID MESSAGE
;
; INITIALIZE THE JMPS TO CP/M BIOS
;
	CALL	INITADR
;
	LDA	FCB+1		;GET PRIMARY OPTION
	CPI	'H'		;MODEM H(ELP)?
	JZ	HELP		;..YES, GIVE HELP
	CPI	' '		;NO OPTIONS?
	JZ	BADOPT		;..EXPLAIN & GIVE HELP
	CPI	'X'		;MODEM X(AMPLES)?
	JZ	EXAM		;GIVE EXAMPLES
;
; SAVE PRIMARY OPTION, VALIDATE SECONDARY OPT.
;
	CALL	PROCOPT
;
; INIT THE MODEM OR SERIAL PORT
;
	CALL	INITMD
;
; MOVE THE FILENAME FROM FCB 2 TO FCB 1
;
	CALL	MOVEFCB
;
; GOBBLE UP GARBAGE CHARS FROM THE LINE
; PRIOR TO RECEIVE OR SEND
;
	CALL	INDATA
	CALL	INDATA
;
; JMP TO APPROPRIATE FUNCTION
;
	LDA	OPTION		;GET PRIMARY OPTION
;
	CPI	'C'		;(COMPAT W/EARLIER
	JZ	TRMECHO 	;OPTION "COMPUTER")
;
	CPI	'E'		;TERMINAL IN ECHO
	JZ	TRMECHO 	;..MODE?
;
	CPI	'T'		;TERMINAL..
	JZ	TERM		;..MODE?
;
	CPI	'D'		;DISCONNECT?
	JZ	DISCONN
;
	CPI	'S'		;SEND..

	IF	TERMNL		;GO TO TERMINAL MODE FIRST.
	JZ	TSND		;..A FILE?
	ENDIF
;
	IF	NOT TERMNL	;GO STRAIGHT TO FILE SEND
	JZ	TERMX		;GO SEND A FILE
	ENDIF
;
	CPI	'R'		;RECEIVE..
;
	IF	TERMNL		;GO TO TERMINAL MODE FIRST
	JZ	TRCV		;..A FILE?
	ENDIF
;
	IF	NOT TERMNL	;GO STRAIGHT TO FILE RECEIVE
	JZ	TERMX		;RECEIVE A FILE
	ENDIF
;
;INVALID OPTION
;
	JMP	BADOPT
;
TSND:	CALL	ILPRT
	DB	'In terminal mode, Ctl-E to start SEND',CR,LF,0
	JMP	TERM
TRCV:	CALL	ILPRT
	DB	'In terminal mode, Ctl-E to start RECEIVE',CR,LF,0
;FALL THRU TO TERM
;
;* * * * * * * * * * * * * * * * * * * * *
;*					 *
;*	TERM: TERMINAL MODE		 *
;*					 *
;* * * * * * * * * * * * * * * * * * * * *
;
; THIS PROGRAM SIMPLY SENDS KEYED CHARACTERS
; DOWN THE LINE, AND DISPLAYS CHARACTERS
; RECEIVED FROM THE LINE.  THIS MAKES IT
; SUITABLE FOR COMMUNICATION WITH TIME SHARING
; COMPUTERS, CBBS'S, OR ANOTHER PROGRAM
; RUNNING "MODEM E" (ECHO MODE)
;
; TYPE THE "EXITCHR" (ORIGINALLY CTL-E) TO LEAVE TERM MODE,
; OR THE "DISCCHR" (ORIGINALLY CTL-D) TO DISCONNECT.
;
; ASCII CAPTURE IS TOGGLED BY ENTERING CTL-Y, AT WHICH TIME
; THE PROGRAM WILL PROMPT FOR THE DESIRED FILE NAME. THE FILE
; IS CLOSED BY AGAIN ENTERING CTL-Y.
;
; THE PRINTER OPTION WORKS JUST AS IN CCP - ENTER CTL-P TO
; TURN THE PRINTER ON, AND AGAIN TO TURN IT OFF.
;
TERM	CALL	STAT		;LOCAL CHAR KEYED?
	JZ	TERML		;..NO, CHECK LINE
	CALL	KEYIN		;GET CHAR
	CPI	EXITCHR 	;TIME TO END?
	JZ	TERMX		;YES, LEAVE TERMINAL MODE
	CPI	DISCCHR 	;DISCONNECT REQUEST?
	JZ	DISCONN 	;YES, DO IT
;
	CPI	CPTRKEY 	;TOGGLE CAPTURE MODE?
	CZ	CPTRTOG 	;YES, DO IT
	JZ	TERM01		;IF CAPTURE MODE COMMAND, DON'T SEND IT
	CPI	PRTCHAR 	;TOGGLE PRINTER MODE?
	CZ	PRTRTOG 	;YES, DO IT
	JZ	TERM01		;IF PRINTER TOGGLE, DON'T SEND IT
;
	IF	TIMESHR
	ORI	80H		;FORCE BIT 7 TO HIGH
	ENDIF			;TIMESHR
;
	CALL	OUTDATA 	;SEND THE CHAR
TERM01	EQU	$
;
; SEE IF CHAR FROM LINE
;
	IF	(NOT DCH) AND (NOT X8250)
TERML	CALL	INCTL		;READ STATUS
	ENDIF
;
	IF X8250
TERML	CALL	INCTL5		;READ STATUS
	ENDIF
;
	IF	DCH
TERML	CALL	INCT2		;READ STATUS
	ENDIF
;
	IF	FRNTPNL AND PMMI
	CALL	OUTPAN		;DISPLAY STATUS
	ENDIF
;
	ANI	MDRCVB		;ISOLATE BIT
	CPI	MDRCVR		;READY?
	JNZ	TERM		;..NO, LOOP
	CALL	INDATA		;READ DATA
	ANI	7FH		;STRIP PARITY BIT
	CALL	TYPE		;TYPE IT
	CALL	PRINTER 	;SEND TO PRINTER IF ENABLED
	CALL	ASCPTR		;SAVE TO DISK IF ENABLED
	JMP	TERM		;LOOP
;
TERMX:	LDA	CPTRFLG
	ORA	A
	CNZ	CPTRTOG	;IF CAPTURE WAS ON, TURN OFF
	LDA	FIRST		;DON'T JUMP
	INR	A		;TO SEND OR RECEIVE
	STA	FIRST		;MORE THAN ONCE
	JNZ	CKDIS		;CHECK DISCONNECT
	LDA	OPTION		;PRIMARY OPTION
	CPI	'S'		;SEND?
	JZ	SENDFIL 	;..A FILE
	CPI	'R'		;RECEIVE
	JZ	RCVFIL		;A FILE
	JMP	CKDIS		;REALLY EXIT
;
;* * * * * * * * * * * * * * * * * * * * *
;*					 *
;*	TRMECHO: TERMINAL WITH ECHO	 *
;*					 *
;* * * * * * * * * * * * * * * * * * * * *
;
; TERMINAL PROGRAM WITH ECHO - SEE NOTES
; UNDER "TERM" ABOVE
;
; C A U T I O N   DON'T RUN WITH BOTH COMPUTERS
; IN "ECHO" MODE - LINE ERRORS (OR ANY CHAR)
; WILL BE ECHOED BACK AND FORTH AD INFINITUM.
;
; ASCII CAPTURE IS TOGGLED BY ENTERING CTL-Y, AT WHICH TIME
; THE PROGRAM WILL PROMPT FOR THE DESIRED FILE NAME. THE FILE
; IS CLOSED BY AGAIN ENTERING CTL-Y.
;
; THE PRINTER OPTION WORKS JUST AS IN CCP - ENTER CTL-P TO
; TURN THE PRINTER ON, AND AGAIN TO TURN IT OFF.
;
	IF	(NOT DCH) AND (NOT X8250)
TRMECHO CALL	INCTL		;GET STATUS
	ENDIF
;
	IF X8250
TRMECHO CALL	INCTL5		;GET STATUS
	ENDIF
;
	IF	DCH
TRMECHO CALL	INCT2		;GET STATUS
	ENDIF
;
	IF	FRNTPNL AND PMMI
	OUT	PANEL		;DISPLAY STATUS
	ENDIF
;
	ANI	MDRCVB		;ISOLATE READY BIT
	CPI	MDRCVR		;ARE WE READY?
	JZ	LINECHR 	;YES, READ THE CHR
	CALL	STAT		;CHECK LOCAL KB
	JZ	TRMECHO 	;..NO CHAR
	CALL	KEYIN		;GET LOCAL CHAR
	CPI	EXITCHR 	;END?
	JZ	CKDIS		;YES, CK DISCONN, EXIT
	CPI	DISCCHR 	;DISCONN?
	JZ	DISCONN 	;..YES, DO IT.
	CPI	CPTRKEY 	;TOGGLE CAPTURE MODE?
	CZ	CPTRTOG 	;..YES, DO IT
	JZ	TERM02		;IF CAPTURE MODE COMMAND, DON'T SEND IT
	CPI	PRTCHAR 	;TOGGLE PRINTER MODE?
	CZ	PRTRTOG 	;..YES, DO IT
	JZ	TERM02		;IF PRINTER TOGGLE, DON'T SEND IT
	CALL	OUTDATA 	;SEND CHAR
	CALL	TYPE		;ECHO IT LOCALLY
	CALL	PRINTER 	;SEND TO PRINTER IF ENABLED
	CALL	ASCPTR		;SAVE IT TO DISK IF ENABLED
TERM02	JMP	TRMECHO 	;..AND LOOP
;
; GOT CHAR FROM LINE
;
LINECHR CALL	INDATA		;GET CHAR
	ANI	7FH		;STRIP PARITY BIT
	CALL	OUTDATA 	;ECHO IT
	CALL	TYPE		;TYPE IT
	CALL	PRINTER 	;SEND TO PRINTER IF ENABLED
	JMP	TRMECHO 	;LOOP
;
;* * * * * * * * * * * * * * * * * * * * *
;*					 *
;*	SENDFIL: SENDS A CP/M FILE	 *
;*					 *
;* * * * * * * * * * * * * * * * * * * * *
;
; THE CP/M FILE SPECIFIED IN THE MODEM COMMAND
; IS TRANSFERRED OVER THE PHONE TO ANOTHER
; COMPUTER RUNNING MODEM WITH THE "R" (RECEIVE)
; OPTION.  THE DATA IS SENT ONE SECTOR AT A
; TIME WITH HEADERS AND CHECKSUMS OR CYCLIC
; REDUNDANCY CHECKS.  CYCLIC REDUNDANCY CHECK
; IS USED IF THE LETTER 'C' IS RECEIVED IN
; PLACE OF THE INITIAL NAK.  IT IS INCORRECT
; TO SPECIFY CRC ON THE SEND (MODEM SC fn.ft),
; SINCE IT IS THE RECEIVER WHO DETERMINES
; WHETHER CRC IS TO BE USED.  IF THERE IS AN
; ERROR, THE SECTOR IS RETRANSMITTED.
;
SENDFIL CALL	TRAP		;CHECK FOR NO NAME OR AMBIG. NAME
	CALL	CNREC		;COMPUTE RECORD COUNT
	CALL	OPENFIL 	;OPEN THE FILE
	MVI	E,80		;WAIT 80 SEC..
	CALL	WAITNAK 	;..FOR INITIAL NAK
;
SENDLP	CALL	RDSECT		;READ A SECTOR
	JC	SENDEOF 	;SEND EOF IF DONE
	CALL	INCRSNO 	;BUMP SECTOR #
	XRA	A		;ZERO ERROR..
	STA	ERRCT		;..COUNT
;
SENDRPT CALL	SENDHDR 	;SEND A HEADER
	CALL	SENDSEC 	;SEND DATA SECTOR
	LDA	CRCFLG		;GET CRC FLAG
	ORA	A		;CRC IN EFFECT?
	CZ	SENDCRC 	;YES, GO SEND CRC
	CNZ	SENDCKS 	;NO, SEND CKSUM NOT CRC
	CALL	GETACK		;GET THE ACK
	JC	SENDRPT 	;REPEAT IF NO ACK
	JMP	SENDLP		;LOOP UNTIL EOF
;
; FILE SENT, SEND EOT'S
;
SENDEOF MVI	A,EOT		;SEND..
	CALL	SEND		;..AN EOT
	CALL	GETACK		;GET THE ACK
	JC	SENDEOF 	;LOOP IF NO ACK
	JMP	DONE		;ALL DONE
;
;---->	CNREC: Computes record count, and saves it
;	       until successful file OPEN.
;
; LOOK UP THE FCB IN THE DIRECTORY
CNREC:	MVI	C,STDMA		;SET DMA ADDR TO
	LXI	D,BASE+80H	; DEF BUFFER
	CALL	BDOS		; SO DIR INFO WHERE SHOULD BE
	MVI	A,'?'		;MATCH ALL EXTENTS
	STA	FCBEXT
	MVI	A,0FFH
	STA	MAXEXT		;INIT MAX EXT NO.
	MVI	C,SRCHF 	;GET 'SEARCH FIRST' FNC
	LXI	D,FCB
	CALL	BDOS		;READ FIRST
	INR	A		;WERE THERE ANY?
	JNZ	SOME		;GOT SOME
	CALL	ERXIT
	DB	'++File not found++',CR,LF,'$'
;
; READ MORE DIRECTORY ENTRIES
MOREDIR MVI	C,SRCHN 	;SEARCH NEXT
	LXI	D,FCB
	CALL	BDOS		;READ DIR ENTRY
	INR	A		;CHECK FOR END (0FFH)
	JNZ	SOME		;NOT END OF DIR...PROCESS EXTENT
	LDA	MAXEXT		;HIT END...GET HIGHEST EXTENT NO. SEEN
	MOV	L,A		;WHICH GIVES EXTENT COUNT - 1
	MVI	H,0
	MOV	D,H
	LDA	RCNT		;GET RECORD COUNT OF MAX EXTENT SEEN
	MOV	E,A		;SAVE IT IN DE
	DAD	H
	DAD	H		;MULTIPLY # OF EXTENTS - 1
	DAD	H		; TIMES 128
	DAD	H
	DAD	H
	DAD	H
	DAD	H
	DAD	D		;ADD IN SIZE OF LAST EXTENT
	SHLD	RCNT		;SAVE TOTAL RECORD COUNT
	RET			;AND EXIT
;
; POINT TO DIRECTORY ENTRY
SOME	DCR	A		;UNDO PREV 'INR A'
	ANI	3		;MAKE MODULUS 4
	ADD	A		;MULTIPLY...
	ADD	A		;..BY 32 BECAUSE
	ADD	A		;..EACH DIRECTORY
	ADD	A		;..ENTRY IS 32
	ADD	A		;..BYTES LONG
	LXI	H,BASE+80H	;POINT TO BUFFER
	ADD	L		;POINT TO ENTRY
	ADI	15		;OFFSET TO RECORD COUNT
	MOV	L,A		;HL NOW POINTS TO REC COUNT
	MOV	B,M		;GET RECORD COUNT
	DCX	H
	DCX	H		;BACK DOWN TO EXTENT NUMBER
	DCX	H
	LDA	MAXEXT		;COMPARE WITH CURRENT MAX.
	ORA	A		;IF NO MAX YET
	JM	BIGGER		;THEN SAVE RECORD COUNT ANYWAY
	CMP	M
	JNC	MOREDIR
;
BIGGER: MOV	A,B		;SAVE NEW RECORD COUNT
	STA	RCNT
	MOV	A,M		;SAVE NEW MAX. EXTENT NO.
	STA	MAXEXT
	JMP	MOREDIR 	;GO FIND MORE EXTENTS
;
;
;* * * * * * * * * * * * * * * * * * * * *
;*					 *
;*	RCVFIL: RECEIVE A FILE		 *
;*					 *
;* * * * * * * * * * * * * * * * * * * * *
;
; RECEIVES A FILE IN BLOCK FORMAT AS SENT
; BY ANOTHER PERSON DOING "MODEM S FN.FT".
; IF THE CRC SECONDARY OPTION (MODEM RC fn.ft)
; WAS CHOSEN, THE LETTER 'C' WILL BE SENT IN
; PLACE OF THE INITIAL NAK.  IF A SECTOR IS
; RECEIVED IN ERROR, THEN A NAK IS SENT WHICH
; REQUESTS THAT THE SECTOR BE RESENT.
;
RCVFIL	CALL	TRAP		;CHECK FOR NO NAME OR AMBIG. NAME
	CALL	ERASFIL 	;ERASE THE FILE
	CALL	MAKEFIL 	;..THEN MAKE NEW
	CALL	ILPRT		;PRINT:
	DB	'File open, ready to receive',CR,LF,0
	LDA	CRCFLG		;GET CRC FLAG
	ORA	A		;CRC BEING USED?
	MVI	A,NAK		;PREPARE FOR CHECKSUM BEING IN EFFECT
	JNZ	RCVFIL2 	;BRANCH IF CHECKSUM BEING USED
	MVI	A,CRC		;REQUEST CYCLIC REDUNDANCY CHECK
;
RCVFIL2 CALL	SEND		;SEND INITIAL NAK (CHECKSUM) OR CRC REQUEST
;
RCVLP	CALL	RCVSECT 	;GET A SECTOR
	JC	RCVEOT		;GOT EOT
	CALL	WRSECT		;WRITE THE SECTOR
	CALL	INCRSNO 	;BUMP SECTOR #
	CALL	SENDACK 	;ACK THE SECTOR
	JMP	RCVLP		;LOOP UNTIL EOF
;
; GOT EOT ON SECTOR - FLUSH BUFFERS, END
;
RCVEOT	CALL	WRBLOCK 	;WRITE THE LAST BLOCK
	CALL	SENDACK 	;ACK THE SECTOR
	CALL	CLOSFIL 	;CLOSE THE FILE
	JMP	DONE		;ALL DONE
;
;* * * * * * * * * * * * * * * * * * * * *
;*					 *
;*		SUBROUTINES		 *
;*					 *
;* * * * * * * * * * * * * * * * * * * * *
;
;---->	TRAP: CHECK FOR NO FILE NAME OR AMBIGUOUS NAME
;
TRAP	LXI	H,FCB+1 	;POINT TO FILE NAME
	MOV	A,M		;GET FIRST CHAR OF FILE NAME
	CPI	' '		;ANY THERE?
	JNZ	ATRAP		;YES, CHECK FOR AMBIGOUS FILE NAME
	CALL	ERXIT		;PRINT MSG, EXIT
	DB	'++No file name specified++',CR,LF,'$'
;
ATRAP	MVI	B,11		;11 CHARS TO CHECK
;
TRLOOP	MOV	A,M		;GET CHAR FROM FCB
	CPI	'?'		;AMBIGUOUS?
	JZ	TRERR		;YES, EXIT WITH ERROR MSG
	INX	H		;POINT TO NEXT CHAR
	DCR	B		;ONE LESS TO GO
	JNZ	TRLOOP		;NOT DONE, CHECK SOME MORE
	RET			;NO AMBIGUOUS NAME, RETURN
;
TRERR	CALL	ERXIT		;PRINT MSG, EXIT
	DB	'++Can''t use wild card options++',CR,LF,'$'
;
;---->	RCVSECT: RECEIVE A SECTOR
;
; RETURNS WITH CARRY SET IF EOT RECEIVED.
;
RCVSECT XRA	A		;GET 0
	STA	ERRCT		;INIT ERROR COUNT
;
RCVRPT	XRA	A		;GET 0
	STA	ERRCDE		;CLEAR RECEIVE ERROR CODE
	LDA	QFLG		;QUIET?
	ORA	A
	JZ	RCVSQ		;YES, NO STAT MSG.
;
	IF	NOT VIDEO	;Then send CRLF
	CALL	CRLF
	ENDIF
;
	IF	VIDEO		;Then send only a CR
	MVI	A,CR
	CALL	TYPE
	ENDIF
;
	CALL	ILPRT		;PRINT:
	DB	'Awaiting # ',0
	PUSH	H		;SAVE HL
	LHLD	SECTNO		;GET SECTOR #
	INX	H		;BUMP IT
	CALL	DECOUT		;PRINT SECTOR # IN DECIMAL
	CALL	ILPRT
	DB	' (',0
	CALL	DHXOUT		;16 BIT HEX CONV & OUTPUT
	CALL	ILPRT
	DB	'H) ',0
	MOV	A,L		;ONLY LOW BYTE USED BY PROG
	POP	H		;RESTORE HL
;
RCVSQ	LDA	FIRSTME 	;GET FIRST TIME SWITCH
	ORA	A		;FIRST TIME THRU?
	JZ	RCVSQ2		;NO, SKIP TO RECEIVE SOH
	XRA	A		;TURN OFF..
	STA	FIRSTME 	;..FIRST TIME SWITCH
	LDA	CRCFLG		;CRC IN..
	ORA	A		;..EFFECT?
	JNZ	RCVSQ2		;NO, DO LONG WAIT FOR SOH
	MVI	B,7		;WAIT FOR UP TO 7 SECONDS
	CALL	RECV		;GET A CHARACTER
	JNC	RCVSQ3		;GOT A CHAR, GO SEE IF SOH
	CALL	ILPRT
	DB	'++Switching to CHECKSUM MODE++',CR,LF,0
	MVI	A,'C'		;TURN OFF...
	STA	CRCFLG		;...CRC MODE
	MVI	A,NAK		;SEND A NAK TO TELL SENDER CHECKSUM..
	CALL	SEND		;..IN EFFECT & START SENDING DATA.
	JMP	RCVSECT 	;GO START RECEIVING SECTOR
;
RCVSQ2	MVI	B,10		;10 SEC TIMEOUT
	CALL	RECV		;GET SOH/EOT
	JC	RCVSTOT 	;TIMEOUT

rcvsq3	CALL	RCVR		;TRANS ERROR?
	JC	RCERR		;CARRY ON IF ERROR
	CPI	SOH		;GET SOH?
	JZ	RCVSOH		;..YES
;
; EARLIER VERSIONS OF MODEM PROG SENT SOME NULLS -
; IGNORE THEM
;
	ORA	A		;00 FROM SPEED CHECK?
	JZ	RCVSQ		;YES, IGNORE IT
	CPI	EOT		;END OF TRANSFER?
	STC			;RETURN WITH CARRY..
	RZ			;..SET IF EOT
;
; DIDN'T GET SOH OR EOT
;
	MOV	B,A		;SAVE CHAR
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..MODE?
	JZ	RCVSEH		;YES, PRT.MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;YES, SKIP MSG
;
RCVSEH	MOV	A,B		;GET CHAR
	CALL	HEXO		;SHOW IN HEX
	CALL	ILPRT		;PRINT:
	DB	'H rcvd, not SOH',CR,LF,0
;
; DIDN'T GET VALID HEADER - PURGE THE LINE,
; THEN SEND NAK.
;
RCVSERR MVI	B,1		;WAIT FOR 1 SEC..
	CALL	RECV		;..WITH NO CHARS
	JNC	RCVSERR 	;LOOP UNTIL SENDER DONE
	LDA	ERRCT		;ABORT IF..
	INR	A		;..WE HAVE REACHED..
	STA	ERRCT		;..THE ERROR..
	CPI	ERRLIM		;..LIMIT?
	JC	RCVCQ2		;..NO, TRY AGAIN (FIRST, SEND NAK)
;
; 10 ERRORS IN A ROW -
;
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..FILE?
	JZ	RCVCKQ		;YES, ASK RETRY/QUIT
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSABT 	;ABORT
;
RCVCKQ	CALL	CKQUIT		;RETRY/QUIT?
	JNZ	RCVSABT 	;QUIT, THEN ABORT
;
; LINE MUST BE PURGED BECAUSE SENDER PROBABLY STARTED
; RESENDING WHILE OPERATOR ANSWERED RETRY/QUIT PROMPT.
;
RCVCQ2	MVI	A,NAK		;SEND NAK TO CANCEL SECTOR
	CALL	SEND
	JMP	RCVRPT		;GO RE-RECEIVE SECTOR
;
RCVSABT CALL	CLOSFIL 	;KEEP WHATEVER WE GOT
	CALL	ERXIT
	DB	'++Unable to receive block -- Aborting++',BELL,CR,LF,'$'
;
; TIMED OUT ON RECEIVE
;
RCVSTOT LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..MODE?
	JZ	RCVSPT		;YES, PRT MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;YES, NO MSG
;
RCVSPT	CALL	ILPRT
	DB	'++Timeout++ ',0
;
RCVPRN	LDA	ERRCT		;PRINT ERROR..
	CALL	HEXO		;..COUNT
	CALL	CRLF
	JMP	RCVSERR 	;BUMP ERR CT, ETC.
;
;---->	RCVR: CHECKS FOR FRAMING ERROR, OVERRUN ERROR,
;	      AND PARITY ERROR.
;	1.  ERROR CODE (ERRCDE) WAS SET IN RECV ROUTINE.
;	2.  ERRCDE=0 FOR NO ERRORS, ERRCDE<>0 FOR ERRORS.
;	3.  IF THERE IS AN ERROR, THE CARRY BIT IS SET ON.
;
RCVR	PUSH	PSW		;SAVE CHAR TRANSMITTED
	LDA	ERRCDE		;GET RECEIVE ERROR CODE
	ANA	A		;IS IT ZERO?
	JZ	RCVR2		;YES, NO RECEIVE ERROR
	POP	PSW		;RESTORE CHAR TRANSMITTED
	STC			;SET CARRY ON TO INDICATE AN ERROR
	RET
;
RCVR2	POP	PSW		;RESTORE CHAR TRANSMITTED
	ORA	A		;CLEAR CARRY BIT
	RET
;
;---->	RCERR: CHECKS FOR A RECEIVE ERROR AND DISPLAYS
;	APROPRIATE ERROR MESSAGE.  THEN GOES TO RCVSERR
;	TO PURGE THE LINE AND SEND A NAK.
;
RCERR	LDA	VSEEFLG 	;VIEWING
	ORA	A		;..MODE?
	JZ	RCERRP		;YES,. PRT MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;YES, NO MSG
;
RCERRP:
	LDA	ERRCDE		;GET RECEIVE ERR CODE
	ANI	FRMER		;WAS THERE A FRAMING ERROR?
	CPI	FRMER
	JNZ	RCERR2		;NO, GO CHECK FOR OVERRUN
	CALL	ILPRT
	DB	'++Framing error++ ',0
	CALL	RCERR5		;PRINT # OF ERR
;
RCERR2:
	LDA	ERRCDE		;GET RECEIVE ERR CODE
	ANI	ORUNER		;WAS THERE AN OVERRUN?
	CPI	ORUNER
	JNZ	RCERR3		;NO, CHECK FOR PARITY ERR
	CALL	ILPRT
	DB	'++Overrun error++ ',0
	CALL	RCERR5
;
RCERR3:
	LDA	ERRCDE		;GET RECEIVE ERR CODE
	ANI	PARER		;WAS THERE A PARITY ERR?
	CPI	PARER
	JNZ	RCERR4		;NO, GO PURGE LINE
	CALL	ILPRT
	DB	'++Parity error++ ',0
	CALL	RCERR5
;
RCERR4:
	JMP	RCVSERR 	;GO PURGE LINE, SEND NAK
;
; DISPLAY THE NUMBER OF THE ERROR, DO A CARRIAGE
; RETURN AND LINE FEED.
;
RCERR5:
	LDA	ERRCT		;GET ERROR NUMBER
	CALL	HEXO		;DISPLAY IT
	CALL	CRLF		;DO CR, LF
	RET
;
;
; GOT SOH - GET BLOCK #, BLOCK # COMPLEMENTED
;
RCVSOH	MVI	B,1		;TIMEOUT = 1 SEC
	CALL	RECV		;GET SECTOR #
	JC	RCVSTOT 	;GOT TIMEOUT
	CALL	RCVR		;TRANSMISSION ERROR?
	JC	RCERR		;YES, GO DISP MSG, PURGE LINE
	MOV	D,A		;D=BLK #
	MVI	B,1		;TIMEOUT = 1 SEC
	CALL	RECV		;GET COMPLEMENTED SECTOR #
	JC	RCVSTOT 	;TIMEOUT
	CALL	RCVR		;TRANSMISSION ERROR?
	JC	RCERR		;YES IF CARRY ON
	CMA			;CALC COMPLEMENT
	CMP	D		;GOOD SECTOR #?
	JZ	RCVDATA 	;YES, GET DATA
;
; GOT BAD SECTOR #
;
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..MODE?
	JZ	RCVBSE		;..YES, PRT MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;..YES, NO MSG
;
RCVBSE	CALL	ILPRT		;PRINT:
	DB	'++Bad sector # in hdr',CR,LF,0
	JMP	RCVSERR 	;BUMP ERROR CT.
;
RCVDATA MOV	A,D		;GET SECTOR #
	STA	RCVSNO		;SAVE IT
	MVI	A,1		;SHOW..
	STA	DATAFLG 	;GETTING DATA
	MVI	C,0		;INIT CKSUM
	CALL	CLRCRC		;CLEAR CRC COUNTER
	LXI	H,BASE+80H	;POINT TO BUFFER
;
RCVCHR	MVI	B,1		;1 SEC TIMEOUT
	CALL	RECV		;GET CHAR
	JC	RCVSTOT 	;TIMEOUT
	CALL	RCVR		;TRANSMISSION ERROR?
	JC	RCERR		;YES IF CARRY ON
	MOV	M,A		;STORE CHAR
	INR	L		;DONE?
	JNZ	RCVCHR		;NO, LOOP
	LDA	CRCFLG		;GET CRC FLAG
	ORA	A		;CRC IN EFFECT?
	JZ	RCVCRC		;YES, GO GET CRC
;
; VERIFY CHECKSUM
;
	MOV	D,C		;SAVE CHECKSUM
	XRA	A		;SHOW..
	STA	DATAFLG 	;..END OF DATA
	MVI	B,1		;TIMEOUT LEN.
	CALL	RECV		;GET CHECKSUM
	JC	RCVSTOT 	;TIMEOUT
	CALL	RCVR		;TRANSMISSION ERROR?
	JC	RCERR		;YES IF CARRY ON
	CMP	D		;CHECKSUM OK?
	JNZ	RCVCERR 	;NO, ERROR
;
; GOT A SECTOR, IT'S A DUP IF = PREV,
; OR OK IF = 1 + PREV SECTOR
;
CHKSNUM LDA	RCVSNO		;GET RECEIVED
	MOV	B,A		;SAVE IT
	LDA	SECTNO		;GET PREV
	CMP	B		;PREV REPEATED?
	JZ	RECVACK 	;ACK TO CATCH UP
	INR	A		;CALC NEXT SECTOR #
	CMP	B		;MATCH?
	JNZ	ABORT		;NO MATCH - STOP SENDER, EXIT
	RET			;CARRY OFF - NO ERRORS
;
; RECEIVE THE CYCLIC REDUNDANCY CHECK CHARACTERS (2 BYTES),
; AND CHECK TO SEE IF THE SENT CRC MATCHES THE CALCULATED
; CRC.	IF THEY MATCH GET NEXT SECTOR, ELSE PRINT ERROR
; MESSAGE IF NOT IN QUIET MODE AND SEND A NAK REQUESTING
; THAT THE SECTOR BE RESENT.
;
RCVCRC	MVI	E,2		;NUMBER OF CRC BYTES TO RECEIVE
;
RCVCRC2 MVI	B,1		;1 SEC TIMEOUT
	CALL	RECV		;GET CRC BYTE
	JC	RCVSTOT 	;CARRY SET IF TIMEOUT
	CALL	RCVR		;TRANSMISSION ERR?
	JC	RCERR		;CARRY SET IF TRANS ERR
	DCR	E		;GOT BOTH CRC BYTES?
	JNZ	RCVCRC2 	;NO, GO GET 2ND BYTE
	CALL	CHKCRC		;CHECK RECVD CRC AGAINST CALCD CRC
	ORA	A		;IS CRC OKAY?
	JZ	CHKSNUM 	;YES, GO CHECK SECTOR NUMBERS
;
;PRINT CRC ERROR MESSAGE
;
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..MODE?
	JZ	RCVCRER 	;..YES, PRINT MESSAGE
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;YES, NO MESSAGE
;
RCVCRER CALL	ILPRT
	DB	'++CRC error++ ',0
	JMP	RCVPRN
;
; GOT CKSUM
;
RCVCERR LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..MODE?
	JZ	RCVCPR		;..YES, PRT MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	RCVSERR 	;YES, NO MSG
;
RCVCPR	CALL	ILPRT
	DB	'++CKSUM error++ ',0
	JMP	RCVPRN		;PRINT ERROR #
;
; PREVIOUS SECTOR REPEATED, DUE TO THE LAST ACK
; BEING GARBAGED.  ACK IT SO SENDER WILL CATCH UP
;
RECVACK CALL	SENDACK 	;SEND THE ACK,
	JMP	RCVSECT 	;GET NEXT BLOCK
;
; SEND AN ACK FOR THE SECTOR
;
SENDACK MVI	A,ACK		;GET ACK
	CALL	SEND		;..AND SEND IT
	RET
;
;---->	SENDHDR: SEND THE SECTOR HEADER
;
; SEND: (SOH) (BLOCK #) (COMPLEMENTED BLOCK #)
;
SENDHDR LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	SENDHNM 	;YES, SKIP STATUS MSG.
;
	IF	NOT VIDEO	;Then send CRLF
	CALL	CRLF
	ENDIF
;
	IF	VIDEO		;Then send only a CR
	MVI	A,CR
	CALL	TYPE
	ENDIF
;
	CALL	ILPRT		;PRINT:
	DB	'Send # ',0
	PUSH	H		;SAVE HL
	LHLD	SECTNO		;GET SECTOR #
	CALL	DECOUT		;PRINT SECTOR # IN DECIMAL
	CALL	ILPRT
	DB	' (',0
	CALL	DHXOUT		;16 BIT HEX CONV & OUTPUT
	CALL	ILPRT
	DB	'H) ',0
	POP	H		;RESTORE HL
;
SENDHNM MVI	A,SOH		;SEND..
	CALL	SEND		;..SOH,
	LDA	SECTNO		;THEN SEND..
	CALL	SEND		;..SECTOR #
	LDA	SECTNO		;THEN SECTOR #
	CMA			;..COMPLEMENTED..
	CALL	SEND		;..SECTOR #
	RET			;FROM SENDHDR
;
;---->	SENDSEC: SEND THE DATA SECTOR
;
;	WHILE SENDING THE SECTOR, THE "DATAFLG" IS SET
;	SUCH THAT IF "V" (VIEW THE FILE) WAS REQUESTED,
;	THE "SHOW" ROUTINE WILL PRINT THE DATA, BUT NOT
;	THE HDR OR CKSUM, OR ANY NON-FATAL MSGS.
;
SENDSEC MVI	A,1		;SHOW NOW AT DATA..
	STA	DATAFLG 	;..FOR VIEW COMMAND
	MVI	C,0		;INIT CKSUM
	CALL	CLRCRC		;CLEAR CRC COUNTER
	LXI	H,BASE+80H	;POINT TO BUFFER
;
SENDC	MOV	A,M		;GET A CHAR
	CALL	SEND		;SEND IT
	INR	L		;POINT TO NEXT CHAR
	JNZ	SENDC		;LOOP IF <100H
	XRA	A		;SHOW NOT INTO DATA..
	STA	DATAFLG 	;..FOR VIEW COMMAND
	RET			;FROM SENDSEC
;
;---->	SENDCKS: SEND THE CHECKSUM
;
SENDCKS MOV	A,C		;SEND THE..
	CALL	SEND		;..CHECKSUM
	RET			;FROM SENDCKS
;
;---->	SENDCRC: CALCULATE THE CYCLIC REDUNDANCY CHECK
;		 AND THEN SEND IT.  FINCRC CALCS THE
;		 FINAL CRC AND PLACES IT IN D,E REGS.
;
SENDCRC CALL	FINCRC		;CALC CRC FOR THE SECTOR
	MOV	A,D		;PUT FIRST CHAR OF CRC IN ACCUM
	CALL	SEND		;SEND IT
	MOV	A,E		;PUT SECOND CHAR OF CRC IN ACCUM
	CALL	SEND
	XRA	A		;MAKE SURE ZERO FLAG IS OFF
	RET
;
;---->	GETACK: GET THE ACK ON THE SECTOR
;
;	RETURNS WITH CARRY CLEAR IF ACK RECEIVED.
;	IF AN ACK IS NOT RECEIVED, THE ERROR COUNT
;	IS INCREMENTED, AND IF LESS THAN "ERRLIM",
;	CARRY IS SET AOD CONTROL RETURNS.  IF THE
;	ERROR COUNT IS AT "ERRLIM", THE PROGRAM
;	ABORTS IF IN "QUIET" MODE, OR ASKS THE
;	USER FOR QUIT/RETRY IF NOT.
;
GETACK	MVI	B,10		;WAIT 10 SECONDS MAX
	CALL	RECVDG		;RECV W/GARBAGE COLLECT
	JC	GETATOT 	;TIMED OUT
	CPI	ACK		;OK? (CARRY OFF IF =)
	RZ			;YES, RET FROM GETACK
	MOV	B,A		;SAVE CHAR
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	ACKERR		;..YES, NO MSG
	MOV	A,B		;GET CHAR
	CALL	HEXO		;PRINT IN HEX
	CALL	ILPRT		;PRINT:
	DB	'H rcvd, not ACK',CR,LF,0
;
; TIMEOUT OR ERROR ON ACK - BUMP ERROR COUNT
;
ACKERR	LDA	ERRCT		;GET COUNT
	INR	A		;BUMP IT
	STA	ERRCT		;SAVE BACK
	CPI	ERRLIM		;AT LIMIT?
	RC			;NOT AT LIMIT
;
; REACHED ERROR LIMIT
;
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..FILE?
	JZ	GACKV		;YES, ASK QUIT/RETRY
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	CSABORT 	;..YES, NO MSG
;
GACKV	CALL	CKQUIT		;SEE IF WANT TO QUIT
	STC			;TO SHOW NO ACK
	RZ			;KEEP ON TRYIN'
;
CSABORT CALL	ERXIT
	DB	'++Can''t send sector -- Aborting++',BELL,CR,LF,'$'
;
; TIMEOUT GETTING ACK
;
GETATOT LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	ACKERR		;YES, NO MSG
	CALL	ILPRT		;PRINT:
	DB	'++Timeout on ACK',CR,LF,0
	JMP	ACKERR
;
ABORT	LXI	SP,STACK
;
ABORTL	MVI	B,1		;1 SEC. W/O CHARS.
	CALL	RECV
	JNC	ABORTL		;LOOP UNTIL SENDER DONE
	MVI	A,NAK		;NEGATIVE ACK
	CALL	SEND		;TELL SENDING END
	CALL	ILPRT		;EXIT WITH ABORT MSG
	DB	'MODEM program cancelled',CR,LF,0
	JMP	CKDIS		;CHECK FOR DISCONN.
;
;---->	INCRSNO: INCREMENT SECTOR #
;
INCRSNO: PUSH	H
	LHLD	SECTNO		;GET SECTOR #
	INX	H		;BOP IT
	SHLD	SECTNO		;STORE UPDATED BACK
	MOV	A,L		;COPY LOW TO ACC FOR CALLERS
	POP	H
	RET
;
;---->	ERASFIL: ERASE THE INCOMING FILE.
;
; IF IT EXISTS, ASK IF IT MAY BE ERASED.
;
ERASFIL LXI	D,FCB		;POINT TO CTL BLOCK
	MVI	C,SRCHF 	;SEE IF IT..
	CALL	BDOS		;..EXISTS
	INR	A		;FOUND?
	RZ			;..NO, RETURN
	CALL	ILPRT		;PRINT:
	DB	'++File exists, type ''Y'' to erase: ',BELL,0
	CALL	KEYIN		;GET CHAR
	PUSH	PSW
	CALL	TYPE		;ECHO
	CALL	CRLF		;BACK TO START OF LINE
	POP	PSW
	ANI	5FH		;MAKE UPPER CASE
	CPI	'Y'		;WANT ERASED?
	JNZ	CKDIS		;QUIT IF NOT ERASE
;
;ERASE OLD FILE
;
	LXI	D,FCB		;POINT TO FCB
	MVI	C,ERASE 	;GET BDOS FNC
	CALL	BDOS		;DO THE ERASE
	RET			;FROM "ERASFIL"
;
;---->	MAKEFIL: MAKES THE FILE TO BE RECEIVED
;
MAKEFIL LXI	D,FCB		;POINT TO FCB
	MVI	C,MAKE		;GET BDOS FNC
	CALL	BDOS		;TO THE MAKE
	INR	A		;FF=BAD?
	RNZ			;OPEN OK
;
; DIRECTORY FULL - CAN'T MAKE FILE
;
	CALL	ERXIT
	DB	'++ERROR -- Can''t make file',CR,LF
	DB	'++Directory must be full',CR,LF,'$'
;
;---->	OPENFIL: OPENS THE FILE TO BE SENT
;
OPENFIL XRA	A		;SET EXT & REC # TO 0 FOR PROPER OPEN
	STA	FCBEXT
	STA	FCBSNO
	LXI	D,FCB		;POINT TO FILE
	MVI	C,OPEN		;GET FUNCTION
	CALL	BDOS		;OPEN IT
	INR	A		;OPEN OK?
	JNZ	OPENOK		;..YES
	CALL	ERXIT		;..NO, ABORT
	DB	'++Can''t open file',CR,LF,'$'
;
OPENOK	CALL	ILPRT		;PRINT:
	DB	'File open - Size: ',0
	LHLD	RCNT		; Get record count.
	CALL	DECOUT		;PRINT DECIMAL NUMBER OF SECTORS
	CALL	ILPRT		;Print:
	DB	' (',0
	CALL	DHXOUT		;Now print size in hex.
	CALL	ILPRT		;PRINT:
	DB	'H) sectors',CR,LF,0
	RET
;
;----> DECOUT: Decimal output routine
;
DECOUT: PUSH	B
	PUSH	D
	PUSH	H
	LXI	B,-10
	LXI	D,-1
;
DECOU2: DAD	B
	INX	D
	JC	DECOU2
	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
	RET
;
;---->	DHXOUT: - double precision hex output routine.
;	Call with hex value in HL.
;
DHXOUT	PUSH	H		;Save H,L
	PUSH	PSW		;Save A
	MOV	A,H		;Get MS byte.
	CALL	HEXO		;Output hi order byte.
	MOV	A,L		;Get LS byte.
	CALL	HEXO		;Output lo order byte.
	POP	PSW		;Restore A
	POP	H		;Restore H,L
	RET			;Return to caller.
;
;
;
;---->	CLOSFIL: CLOSES THE RECEIVED FILE
;
CLOSFIL LXI	D,FCB		;POINT TO FILE
	MVI	C,CLOSE 	;GET FUNCTION
	CALL	BDOS		;CLOSE IT
	INR	A		;CLOSE OK?
	RNZ			;..YES, RETURN
	CALL	ERXIT		;..NO, ABORT
	DB	BELL,'++Can''t close file',CR,LF,'$'
;
;--->	ASCPTR - CHECK TO SEE IF ASCII CAPTURE IS ENABLED-IF
;		 IT IS, SAVE THE INCOMING CHARACTER IN THE
;		 MEMORY BUFFER, WRITING IT WHEN FULL
;
ASCPTR:	PUSH	PSW		;SAVE CHARACTER
	LDA	CPTRFLG 	;LOOK AT CAPTURE FLAG
	ORA	A		;CAPTURE ENABLED?
	JZ	ASCQEND 	;  NO, TAKE QUICK EXIT
	POP	PSW		;GET THE CHARACTER
	PUSH	PSW		;  AND SAVE ALL THE REGISTERS
	PUSH	B
	PUSH	D
	PUSH	H
	LHLD	CAPPTR		;GET THE BUFFER POINTER
	MOV	M,A		;  PUT CHARACTER IN BUFFER
	INX	H		;  INCREMENT POINTER
	SHLD	CAPPTR		;  AND SAVE IT
	LDA	MEMEND		;GET LAST PAGE ADDRESS
	CMP	H		;ARE WE THERE YET?
	JZ	ASCPTR1 	;  YES, TIME TO WRITE THE BUFFER
	DCR	A		;NEXT TO LAST PAGE ADDRESS
	CMP	H		;NEARING THE END?
	JNZ	ASCEND		;  NO, EXIT ROUTINE
	MOV	A,L		;CHECK LOWER BYTE OF ADDRESS
	CPI	-STPTIME AND 0FFH
	JNZ	ASCEND		;NOT TIME FOR STOP CHAR, SO EXIT
	MVI	A,STOPCHR	;TELL OTHER END TO STOP
	CALL	OUTDATA 	;  IN ADVANCE
	JMP	ASCEND
ASCPTR1 LXI	D,ASCBUF
	LDA	MEMEND
	SUB	D
	MOV	B,A		;NUMBER OF PAGES TO SAVE
ASCPTR2 MOV	C,2		;NUMBER OF SECTORS/PAGE
ASCPTR3 CALL	AWRITE
	LXI	H,128
	DAD	D
	XCHG			;MOVE TO NEXT SECTOR TO WRITE
	DCR	C
	JNZ	ASCPTR3 	;UNTIL PAGE IS WRITTEN
	DCR	B
	JNZ	ASCPTR2 	;UNTIL ALL PAGES ARE WRITTEN
	LXI	H,ASCBUF	;RESET BUFFER POINTER
	SHLD	CAPPTR
	MVI	A,STRTCHR	;TELL OTHER END TO START
	CALL	OUTDATA
ASCEND	POP	H
	POP	D
	POP	B
ASCQEND POP	PSW
	RET
AWRITE	PUSH	D
	MVI	C,STDMA 	;SET BUFFER ADDRESS
	CALL	BDOS
	LXI	D,ASCFCB
	MVI	C,21		;WRITE SEQUENTIAL
	CALL	BDOS
	INR	A
	JNZ	AWRITE1
	CALL	ILPRT
	DB	BELL,'++Out of disk space',CR,LF,0
AWRITE1 POP	D
	RET
;
;---->	RDSECT: READS A SECTOR
;
;	FOR SPEED, THIS ROUTINE BUFFERS UP DBUFSIZ*8
;	SECTORS AT A TIME.
;
RDSECT	LDA	SECINBF 	;GET # SECT IN BUFF.
	DCR	A		;DECREMENT..
	STA	SECINBF 	;..IT
	JM	RDBLOCK 	;EXHAUSTED?  NEED MORE.
	LHLD	SECPTR		;GET POINTER
	LXI	D,BASE+80H	;TO DATA
	CALL	MOVE128 	;MOVE TO BUFFER
	SHLD	SECPTR		;SAVE BUFFER POINTER
	RET			;FROM "READSEC"
;
; BUFFER IS EMPTY - READ IN ANOTHER BLOCK OF DBUFSIZ*8
;
RDBLOCK LDA	EOFLG		;GET EOF FLAG
	CPI	1		;IS IT SET/
	STC			;TO SHOW EOF
	RZ			;GOT EOF
	MVI	C,0		;SECTORS IN BLOCK
	LXI	D,DBUF		;TO DISK BUFFER
;
RDSECLP PUSH	B
	PUSH	D
	MVI	C,STDMA 	;SET DMA..
	CALL	BDOS		;..ADDR
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	POP	D
	POP	B
	ORA	A		;READ OK?
	JZ	RDSECOK 	;YES
	DCR	A		;EOF?
	JZ	REOF		;GOT EOF
;
; READ ERROR
;
	CALL	ERXIT
	DB	BELL,'++File read error',CR,LF,'$'
;
RDSECOK LXI	H,80H		;ADD LENGTH OF ONE SECTOR...
	DAD	D		;...TO NEXT BUFF
	XCHG			;BUFF TO DE
	INR	C		;MORE SECTORS?
	MOV	A,C		;GET COUNT
	CPI	DBUFSIZ*8	;DONE?
	JZ	RDBFULL 	;..YES, BUFF IS FULL
	JMP	RDSECLP 	;READ MORE
;
REOF	MVI	A,1
	STA	EOFLG		;SET EOF FLAG
	MOV	A,C
;
; BUFFER IS FULL, OR GOT EOF
;
RDBFULL STA	SECINBF 	;STORE SECTOR COUNT
	LXI	H,DBUF		;INIT BUFFER..
	SHLD	SECPTR		;..POINTER
	LXI	D,BASE+80H	;RESET..
	MVI	C,STDMA 	;..DMA..
	CALL	BDOS		;..ADDR
	JMP	RDSECT		;PASS SECT TO CALLER
;
;---->	WRSECT: WRITE A SECTOR
;
;	WRITES THE SECTOR INTO A BUFFER.  WHEN DBUFSIZ*8
;	HAVE BEEN WRITTEN, WRITES THE BLOCK TO DISK.
;
;	ENTRY POINT "WRBLOCK" FLUSHES THE BUFFER AT EOF.
;
WRSECT	LHLD	SECPTR		;GET BUFF ADDR
	XCHG			;TO DE FOR MOVE
	LXI	H,BASE+80H	;FROM HERE
	CALL	MOVE128 	;MOVE TO BUFFER
	XCHG			;SAVE NEXT..
	SHLD	SECPTR		;..BLOCK POINTER
	LDA	SECINBF 	;BUMP THE..
	INR	A		;..SECTOR #..
	STA	SECINBF 	;..IN THE BUFF
	CPI	DBUFSIZ*8	;HAVE WE DBUFSIZ*8?
	RNZ			;NO, RETURN
;
;---->	WRBLOCK: WRITES A BLOCK TO DISK
;
WRBLOCK LDA	SECINBF 	;# SECT IN BUFFER
	ORA	A		;0 MEANS END OF FILE
	RZ			;NONE TO WRITE
	MOV	C,A		;SAVE COUNT
	LXI	D,DBUF		;POINT TO DISK BUFF
;
DKWRLP	PUSH	H
	PUSH	D
	PUSH	B
	MVI	C,STDMA 	;SET DMA
	CALL	BDOS		;TO BUFFER
	LXI	D,FCB		;THEN WRITE
	MVI	C,WRITE 	;..THE..
	CALL	BDOS		;..BLOCK
	POP	B
	POP	D
	POP	H
	ORA	A
	JNZ	WRERR		;OOPS, ERROR
	LXI	H,80H		;LENGTH OF 1 SECT
	DAD	D		;HL= NEXT BUFF
	XCHG			;TO DE FOR SETDMA
	DCR	C		;MORE SECTORS?
	JNZ	DKWRLP		;..YES, LOOP
	XRA	A		;GET A ZERO
	STA	SECINBF 	;RESET # OF SECTORS
	LXI	H,DBUF		;RESET BUFFER..
	SHLD	SECPTR		;..POINTER
;
RSDMA	LXI	D,BASE+80H	;RESET..
	MVI	C,STDMA 	;..DMA..
	CALL	BDOS		;..ADDR
	RET
;
WRERR	CALL	RSDMA		;RESET DMA TO NORM.
	CALL	ILPRT		;PRINT:
	DB	BELL,'++Error writing file',CR,LF,0
	JMP	ABORT		;EXIT
;
;---->	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.
;
RECVDG	EQU	$		;RECEIVE W/GARBAGE DELETE
	CALL	INDATA		;GET A CHAR
	CALL	INDATA		;..TOTALLY PURGE UART
;
RECV	PUSH	D		;SAVE
;
	IF	FASTCLK 	;4MHZ?
	MOV	A,B		;GET TIME REQUEST
	ADD	A		;DOUBLE IT
	MOV	B,A		;NEW TIME IN B
	ENDIF
;
MSEC	LXI	D,50000 	;1 SEC DCR COUNT
;
	IF	(NOT DCH) AND (NOT X8250)
MWTI	CALL	INCTL		;CHECK STATUS
	ENDIF
;
	IF	X8250
MWTI	CALL	INCTL5		;CHECK STATUS
	ENDIF
;
	IF	DCH
MWTI	CALL	INCT2		;CHECK STATUS
	ENDIF
;
	IF	FRNTPNL AND PMMI
	CALL	OUTPAN		;DISPLAY STATUS
	ENDIF
;
	ANI	MDRCVB		;ISOLATE BIT
	CPI	MDRCVR		;READY?
	JZ	MCHAR		;GOT CHAR
	DCR	E		;COUNT..
	JNZ	MWTI		;..DOWN..
	DCR	D		;..FOR..
	JNZ	MWTI		;..TIMEOUT
	DCR	B		;MORE SECONDS?
	JNZ	MSEC		;YES, WAIT
;
; MODEM TIMED OUT RECEIVING
;
	POP	D		;RESTORE D,E
	STC			;CARRY SHOWS TIMEOUT
	RET
;
; GOT CHAR FROM MODEM
;
; CHECK TO SEE IF THERE WAS A FRAMING, OVERRUN,
; AND/OR PARITY ERROR.
;
MCHAR:	IF	(NOT DCH) AND (NOT X8250)
	CALL	INCTL		;GET STATUS
	ENDIF
;
	IF	X8250
	CALL	INCTL5		;GET STATUS
	ENDIF
;
	IF	DCH
	CALL	INCT2		;GET STATUS
	ENDIF
;
	MOV	D,A		;SAVE STATUS
	ANI	FRMER		;FRAMING ERR?
	JZ	MCHAR2		;NO, CHECK FOR OVERRUN
	LDA	ERRCDE		;GET RECV ERR CODE
	ORI	FRMER		;TURN ON RECV ERR CODE
	STA	ERRCDE
;
MCHAR2: MOV	A,D		;RESTORE STATUS
	ANI	ORUNER		;OVERRUN ERR?
	JZ	MCHAR3		;NO, CHECK FOR PARITY
	LDA	ERRCDE		;GET RECV ERR CODE
	ORI	ORUNER		;TURN ON RECV ERR CODE
	STA	ERRCDE
;
MCHAR3: MOV	A,D		;RESTORE STATUS
	ANI	PARER		;PARITY ERR?
	JZ	MCHAR4		;NO, GET DATA CHAR
	LDA	ERRCDE		;GET RECV ERR CODE
	ORI	PARER		;TURN ON RECV ERR CODE
	STA	ERRCDE
;
; Check for error and call reset code if yes
;
MCHAR4:
	LDA	ERRCDE		; Retrieve error code
	ORA	A		; Set flags
	CNZ	RESRCVR		; Reset if necessary
;
; GET THE DATA CHAR
;
	CALL	INDATA		;GET MODEM DATA CHAR
	POP	D		;RESTORE DE
;
; CALC CHECKSUM AND CYCLIC REDUNDANCY CHECK
;
	PUSH	PSW		;SAVE THE CHAR
	CALL	UPDCRC		;CALC CYCLIC REDUNDANCY CHECK
	ADD	C		;ADD TO CHECKSUM
	MOV	C,A		;SAVE CHECKSUM
;
; CHECK IF MONITORING REC'D DATA
;
	LDA	RSEEFLG 	;SEE RECEIVED..
	ORA	A		;..DATA?
	JZ	MONIN		;..YES
;
; CHECK IF "VIEWING" AND THIS IS A DATA CHAR
;
	LDA	VSEEFLG 	;VIEWING..
	ORA	A		;..DATA?
	JNZ	NOMONIN 	;..NO
;
; "VIEW" REQUESTED.  SHOW THE CHAR IF IT IS DATA
;
	LDA	DATAFLG 	;GET DATA FLAG
	ORA	A		;TEST IT
	JZ	NOMONIN 	;..OFF, NOT DATA
;
MONIN	POP	PSW		;..IS DATA,
	PUSH	PSW		;GET IT,
	CALL	SHOW		;..AND SHOW IT
;
NOMONIN POP	PSW		;RESTORE CHAR
	ORA	A		;CARRY OFF: NO ERROR
	RET			;FROM "RECV"
;
; This is the section of code that resets the error
; bit in the receiving device. (from parity, framing,
; and overrun)
; NOTE: I have implemented the code for an Intel 8251
;	usart, but since I know nothing about other
;	commonly used devices, you must add your own
;	reset code here in a conditional.
;
RESRCVR:
	IF	X8251
	MVI	A,RSTCODE	; Load reset code
	CALL	OUTCTL		; Send to control port
	ENDIF			; X8251

;  Add code here for other than 8251 !!!!

	RET		; <=== This "RET" must be here
			;  outside the conditional !

;
;---->	SEND: SEND A CHARACTER TO THE MODEM
;
;	THE CHARACTER TO BE SENT IS SAVED
;	IN REG D.  IF VIEWING IS IN EFFECT,
;	THE CHARACTER IS DISPLAYED.  THEN
;	BOTH THE CHECKSUM AND CRC ARE CALCU-
;	LATED.
;
SEND	PUSH	D		;SAVE D,E REGS
	MOV	D,A		;SAVE THE CHAR
;
; CHECK IF MONITORING SENT DATA
;
	LDA	SSEEFLG 	;CHECK IF MONITORING..
	ORA	A		;..SENT DATA
	JZ	MONOUT		;..YES
;
; CHECK IF "VIEWING" THE FILE
;
	LDA	VSEEFLG 	;GET VIEW FLAG
	ORA	A		;TEST IT
	JNZ	NOMONOT 	;NO
	LDA	DATAFLG 	;IS THIS
	ORA	A		;..DATA?
	JZ	NOMONOT 	;..NO.
;
MONOUT	MOV	A,D		;GET THE CHAR
	CALL	SHOW		;SHOW IT
;
NOMONOT MOV	A,D		;RESTORE THE CHAR
	CALL	UPDCRC		;CALC CRC
	ADD	C		;CALC CKSUM
	MOV	C,A		;SAVE CKSUM
;
	IF	(NOT DCH) AND (NOT X8250)
SENDW	CALL	INCTL		;GET STATUS
	ENDIF
;
	IF	X8250
SENDW	CALL	INCTL5		;GET STATUS
	ENDIF
;
	IF	DCH
SENDW	CALL	INCT2		;GET STATUS
	ENDIF
;
	IF	FRNTPNL
	CALL	OUTPAN		;DISPLAY STATUS
	ENDIF
;
	ANI	MDSNDB		;ISOLATE READY BIT
	CPI	MDSNDR		;READY?
	JNZ	SENDW		;..NO, WAIT
	MOV	A,D		;GET CHAR
	POP	D		;RESTORE D,E
	CALL	OUTDATA 	;OUTPUT IT
	RET			;FROM "SEND"
;
;---->	WAITNAK: WAITS FOR INITIAL NAK
;
;	TO ENSURE NO DATA IS SENT UNTIL THE RECEIVING
;	PROGRAM IS READY, THIS ROUTINE WAITS FOR
;	THE FIRST TIMEOUT-NAK FROM THE RECEIVER/
;	(E) CONTAINS THE # OF SECONDS TO WAIT.
;
WAITNAK LDA	VSEEFLG 	;VIEWING?
	ORA	A
	JZ	WAITNPR 	;PRINT MSG
	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	WAITNLP 	;YES, SKIP MSG
;
WAITNPR CALL	ILPRT		;PRINT:
	DB	'Awaiting initial NAK or CRC request',CR,LF,0
;
WAITNLP MVI	B,1		;TIMEOUT DELAY
	CALL	RECV		;DID WE GET..
	CPI	NAK		;..A NAK?
	RZ			;YES, SEND BLOCK
	CPI	CRC		;CRC REQUEST?
	JZ	WAITCRC 	;YES, GO SET CRC FLAG
	DCR	E		;80 TRIES?
	JZ	ABORT		;YES, ABORT
	JMP	WAITNLP 	;NO, LOOP
;
WAITCRC CALL	ILPRT
	DB	'CRC request received',CR,LF,0
	XRA	A		;ZERO ACCUM
	STA	CRCFLG		;INDICATE CRC IN EFFECT
	RET
;
;---->	INITADR: INIT'S CP/M BIOS ADDRESSES
;
;	THIS ROUTINE FILLS IN THE ADDRESSES OF VARIOUS
;	JMP AND CALL INSTRUCTIONS, SO THAT CP/M BDOS
;	IS BYPASSED WHILE ACCESSING THE CONSOLE.  THIS
;	IS DONE TO ALLOW CHARACTERS SUCH AS CONTROL-C
;	AND CONTROL-S TO BE KEYED WHILE IN TERMINAL
;	MODE, WITHOUT CP/M INTERPRETING THEM.
;
INITADR LHLD	BASE+1		;GET WARM BOOT ADDR
	LXI	D,3		;LENGTH OF A 'JMP'
	DAD	D		;TO CONSOLE STAT
	SHLD	VSTAT+1 	;MODIFY CALL
	DAD	D		;TO CONSOLE IN
	SHLD	VKEYIN+1	;MODIFY CALL
	DAD	D		;TO CONSOLE OUT
	SHLD	VTYPE+1 	;MODIFY CALL
	RET
;
;---->	CPTRTOG - toggles ASCII capture mode on and off
;
CPTRTOG PUSH	H		;SAVE ALL REGISTERS
	PUSH	D
	PUSH	B
	PUSH	PSW
	MVI	A,STOPCHR
	CALL	OUTDATA
	LDA	CPTRFLG 	;GET CAPTURE FLAG
	CMA			;  INVERT IT
	STA	CPTRFLG 	;  AND SAVE IT
	ORA	A		;DO WE OPEN OR CLOSE A FILE?
	JZ	CPTRCLS 	;  CAPTURE DISABLED, SO CLOSE
	LXI	H,ASCBUF	;INITIALIZE BUFFER STARTING ADDRESS
	SHLD	CAPPTR
GETNAM	CALL	ILPRT
	DB	CR,LF,'Enter ASCII capture file name --> ',BELL,0
	LXI	H,ASCFCB
	MVI	M,0		;DEFAULT DRIVE IF NOT SPECIFIED
;
	INX	H		;POINT TO FILE NAME
	MVI	B,11		;FILL NAME AND TYPE WITH BLANKS
	MVI	A,' '
CPTRT01 MOV	M,A
	INX	H
	DCR	B
	JNZ	CPTRT01
;
	LXI	H,ASCBUF	;GET FILE NAME FROM USER
	MVI	M,15		;MAX NUMBER OF CHARACTERS TO ACCEPT
	XCHG
	MVI	C,10		;USE READ CONSOLE BUFFER FUNCTION
	CALL	BDOS		;TO GET INPUT
;
	MVI	E,LF		;PUT USER BACK ON CLEAN LINE
	MVI	C,2
	CALL	BDOS
;
	LXI	H,ASCBUF
	INX	H
	MOV	B,M		;NUMBER OF CHARACTERS INPUT
	MOV	A,B		;MAKE SURE SOMETHING WAS INPUT
	CPI	0		;IF NOTHING INPUT, THEN
	JZ	NULNAM		;  ASK FOR FILENAME AGAIN...
	MVI	C,0		;NUMBER OF FCB CHARS FILLED
	INX	H
	INX	H		;LOOK AT 2ND CHARACTER INPUT
	MOV	A,M
	CPI	':'		;DRIVE CODE SPECIFIED?
	DCX	H		;POINT BACK TO 1ST CHAR INPUT
	JNZ	CPTRSET 	;SKIP IF NO DRIVE SPECIFIED
	MOV	A,M		;GET DRIVE CODE
	CALL	VFYCHR		;LEGAL CHARACTER ????
	CPI	'A'
	JM	BADDRV		;JUMP IF < A:
	CPI	MAXDRV+1
	JP	BADDRV		;JUMP IF > MAXDRV
	ANI	0FH		;CONVERT FROM ASCII
	STA	ASCFCB		;PUT IT IN THE FCB
	INX	H
	INX	H		;POINT TO FIRST CHAR OF FILE NAME
	DCR	B
	DCR	B		;ADJUST CHAR COUNT
	JZ	NONAME
CPTRSET LXI	D,ASCFCB+1	;START OF FILE NAME IN FCB
CPTRSE1 MOV	A,M		;GET CHAR
	CPI	'.'		;END OF FILE NAME?
	JZ	CPTRTYP
	CALL	VFYCHR		;LEGAL CHARACTER ????
	STAX	D		;NO, STORE IT
	INX	H
	INX	D
	INR	C
	DCR	B
	JNZ	CPTRSE1 	;LOOP IF MORE CHARS WERE INPUT
	JMP	CPTROPN 	;NO FILE TYPE GIVEN, SO USE BLANK DEFAULT
;
CPTRTYP	INX	H		;MOVE PAST PERIOD IN SPECIFIED NAME
	LXI	D,ASCFCB+9	;POINT TO FILE TYPE IN FCB
	DCR	B		;ACCOUNT FOR THE SKIPPED PERIOD
	JZ	CPTROPN 	;IF NO MORE, SKIP IT
	MOV	A,M		;GET THE CHAR
	CALL	VFYCHR		;LEGAL CHARACTER ????
	STAX	D		;  AND SAVE IT
	INX	H
	INX	D
	DCR	B
	JZ	CPTROPN 	;SKIP IF NO MORE
	MOV	A,M		;GET NEXT CHAR
	CALL	VFYCHR		;LEGAL CHARACTER ????
	STAX	D		;  AND SAVE IT
	INX	H
	INX	D
	DCR	B
	JZ	CPTROPN 	;SKIP IF NO MORE
	MOV	A,M		;GET LAST CHAR
	CALL	VFYCHR		;LEGAL CHARACTER ????
	STAX	D		;  AND SAVE IT
CPTROPN	PUSH	H
	LXI	H,ASCFCB+1	;POINT TO 1ST CHAR. IN NAME
	MOV	A,M
	POP	H
	CPI	' '
	JZ	NONAME		;IF = ' ' THEN NO NAME GIVEN
	MVI	A,0
	STA	ASCFCB+12	;CLEAR EXTENT
	STA	ASCFCB+32	;  AND CURRENT RECORD
	LXI	D,ASCFCB	;SEE IF THERE ALREADY
	MVI	C,SRCHF
	CALL	BDOS
	INR	A
	JZ	CPTRNF		;PASS IF NOT
	CALL	ILPRT
	DB	'++File exists, type ''Y'' to erase: ',BELL,0
	CALL	KEYIN		;GET RESPONSE
	PUSH	PSW		;SAVE IT
	CALL	TYPE		;ECHO IT
	CALL	CRLF		; THEN EJECT LINE
	POP	PSW
	ANI	5FH		;FOLD TO UPPER CASE
	CPI	'Y'		;GOT A YES?
	JNZ	GETNAM		;ASK AGAIN IF NOT
	LXI	D,ASCFCB	;PRE-DELETE FILE
	MVI	C,ERASE
	CALL	BDOS
;
CPTRNF:	LXI	D,ASCFCB
	MVI	C,22		; MAKE FILE
	CALL	BDOS
	INR	A		;0FFH ERROR CODE BECOMES 00H
	JNZ	CPTROP2 	;SKIP IF NO ERROR
	CALL	ILPRT
	DB	CR,LF,'++Directory full',CR,LF,0
	XRA	A
	STA	CPTRFLG
	JMP	CPTRRET
CPTROP2	CALL	ILPRT
	DB	CR,LF,'ASCII capture now activated',CR,LF,0
	JMP	CPTRRET
CPTRCLS LHLD	CAPPTR		;GET CURRENT BUFFER POINTER
CPTRCL1 MVI	M,1AH		;FILL OUT THE RECORD WITH CTL-Z
	MOV	A,L
	ANI	7FH		;ARE WE ON A MULTIPLE OF 128?
	JZ	CPTRCL2 	;  YES, EXIT LOOP
	INX	H		;ELSE INCREMENT POINTER
	JMP	CPTRCL1 	;  AND CONTINUE
CPTRCL2 LXI	D,ASCBUF	;START OF BUFFER
	MOV	A,D		;IF THE BUFFER IS EMPTY
	CMP	H		;  THEN DON'T WRITE ANYTHING
	JNZ	CPTRCL3 	;  AND CLOSE THE FILE
	MOV	A,E
	CMP	L
	JZ	CPTRCL4
CPTRCL3 CALL	AWRITE		;WRITE A RECORD
	LXI	H,128		;INCREMENT WRITE POINTER BY 1 SECTOR
	DAD	D
	XCHG
	LDAX	D		;GET THE FIRST CHAR OF NEXT SECTOR
	CPI	1AH		;END OF FILE?
	JNZ	CPTRCL3 	;  IF NOT, WRITE SOME MORE
CPTRCL4 LXI	D,ASCFCB
	MVI	C,16		; CLOSE FILE
	CALL	BDOS
	INR	A		;0FFH ERROR CODE BECOMES 00H
	JZ	CPTRCL5 	;SKIP IF ERROR
	CALL	ILPRT
	DB	CR,LF,'ASCII capture now off, '
	DB	'file closed',CR,LF,0
	JMP	CPTRRET
CPTRCL5	CALL	ILPRT
	DB	CR,LF,'++Error in closing file',CR,LF,0
CPTRRET MVI	A,STRTCHR
	CALL	OUTDATA
	POP	PSW
	POP	B
	POP	D
	POP	H
	RET
;
;---->  VFYCHR - CONVERT LOWER CASE TO UPPER, AND TRAP
;---->		 ALL ILLEGAL CHARACTERS IN FILE NAME
VFYCHR	ANI	7FH		;FORCE PARITY OFF
	CPI	061H
	JM	VFYCH1		;BEGIN LOWER->UPPER
	CPI	7BH
	JP	VFYCH1
	SUI	20H		;WAS LOWER, FIX IT
VFYCH1	CPI	' '
	JM	BADNAM		;NO CTRL CHARS IN FILE NAME
	CPI	'9'+1
	JM	VFYCH2
	CPI	'@'
	JM	BADNAM
VFYCH2	CPI	5FH
	JP	BADNAM
	CPI	'.'
	JZ	BADNAM
	CPI	','
	JZ	BADNAM
	CPI	'*'
	JZ	BADNAM
	CPI	'['
	JZ	BADNAM
	CPI	']'
	JZ	BADNAM
	RET			;GOOD CHARACTER !!!!
	;
BADNAM	CALL	ILPRT
	DB	'++Invalid character in '
	DB	'file name++',BELL,CR,LF,LF,0
	POP	H		;REMOVE LAST CALL
	JMP	GETNAM
	;
NULNAM	CALL	ILPRT
	DB	'++Null file name specified,'
	DB	' ASCII capture not activated'
	DB	'++',CR,LF,LF,0
	XRA	A
	STA	CPTRFLG		;FORCE CPATURE OFF
	JMP	CPTRRET		;EXIT
	;
BADDRV	CALL	ILPRT
	DB	'++Invalid drive specified, '
	DB	'try again++',BELL,CR,LF,0
	JMP	GETNAM
	;
NONAME	CALL	ILPRT
	DB	'++Can''t specify filetype '
	DB	'without a filename++',BELL,CR,LF,0
	JMP	GETNAM
	;
	;
;---->	PRINTER - SEND A CHARACTER TO THE PRINTER
;
PRINTER PUSH	H		;SAVE REGISTERS
	PUSH	D
	PUSH	B
	PUSH	PSW
	LDA	PRTRFLG 	;GET PRINTER FLAG
	ORA	A		;ENABLED?
	JZ	PRTREND 	;NO, SKIP IT
	POP	PSW		;GET THE CHAR
	PUSH	PSW		;  AND SAVE IT AGAIN
	MOV	E,A
	MVI	C,5
	CALL	BDOS		;PRINT IT
PRTREND POP	PSW		;RESTORE REGISTERS
	POP	B
	POP	D
	POP	H
	RET
;
;---->	PRTRTOG - ENABLE/DISABLE THE PRINTER
;
PRTRTOG PUSH	PSW
	LDA	PRTRFLG
	CMA
	STA	PRTRFLG
	POP	PSW
	RET
;
;---->	PROCOPT: PROCESS COMMAND OPTIONS
;
;	1) SAVED THE PRIMARY OPTION IN 'OPTION';
;	2) SCANS THE SUB-OPTION CHARACTERS, AND FOR
;	   EACH FOUND, ZEROS THE APPROPRIATE ENTRY IN
;	   THE OPTION TABLE.  FOR EXAMPLE, IF 'Q' IS
;	   CODED (QUIT/DISCONNECT) THEN THE 'D' STORED AT
;	   'DISCFLG' IS SET TO 0 SO IT CAN BE TESTED
;	   LATER.
;
PROCOPT LXI	D,FCB+1 	;TO PRIMARY OPT.
	LDAX	D		;GET PRIMARY
	STA	OPTION		;SAVE IT
;
OPTLP	INX	D		;TO SECONDARY OPTION
	LDAX	D		;GET CHAR
;
; IF YOU MOD THIS PROGRAM FOR >7 OPTIONS,
; YOU MUST CHANGE THE FOLLOWING, SINCE
; THERE WON'T BE A ' ' AFTER THE OPTION
; IF A BAUD RATE WAS SPECIFIED.
;
	CPI	' '		;NO MORE OPT'NS?
	JZ	ENDOPT		;..YES
;
; SET THE APPROP. OPTION: STORE 0 IN IT
;
	LXI	H,OPTBL 	;HL = ADDR OF 'OAQDSRV'
	MVI	B,OPTBE-OPTBL	;OPT TABLE LEN
;
OPTCK	CMP	M		;FOUND THE OPTION?
	JNZ	OPTNO		;NO, DON'T SET IT
	MVI	M,0		;SET THE OPTION
	JMP	OPTLP		;GET NEXT OPTION
;
OPTNO	INX	H		;TO NEXT
	DCR	B		;MORE?
	JNZ	OPTCK
;
; OPTION NOT IN TABLE
;
	JMP	BADOPT		;SHOW BAD SUB OPTION
;
ENDOPT	LDA	CRCFLG		;GET CRC FLAG
	ORA	A		;CRC IN EFFECT?
	JNZ	ENDOPT2 	;NO, EVERYTHING OKAY
	LDA	OPTION		;GET PRIMARY OPTION
	CPI	'R'		;WAS IT 'FILE RECEIVE'
	JNZ	BADOPT		;NO, CRC ONLY ALLOWED FOR RECV
;
; IF "VIEW" WAS ASKED FOR, SET QUIET FLAG
;
ENDOPT2 LDA	VSEEFLG 	;VIEW..
	ORA	A		;..ASKED FOR?
	RNZ			;..NO, RET FROM 'PROCOPT'
	STA	QFLG		;YES, NO HDR/CKSUM PRT
	RET			;FROM 'PROCOPT'
;
; DONE - CLOSE UP SHOP
;
DONE	LDA	VSEEFLG 	;VIEWING?
	ORA	A
	JZ	DONETC		;SHOW MSG
	LDA	QFLG		;QUIET
	ORA	A		;..MODE?
	JZ	DONECTE 	;YES, CK TERM/ECHO
;
DONETC	CALL	ILPRT
	DB	BELL,CR,LF,'Transfer complete'
	DB	CR,LF,BELL,0
;
; CHECK IF TERMINAL OR ECHO SUB COMMAND
; WAS SPECIFIED
;
DONECTE LDA	TERMFLG 	;TERM?
	ORA	A
	JZ	TERM		;..YES
	LDA	ECHOFLG 	;ECHO?
	ORA	A
	JZ	TRMECHO 	;..YES
;
; FALL INTO 'CKDIS'
;
;---->	CKDIS: CHECK IF DISCONNECT REQUESTED
;
;	THIS ROUTINE IS JUMPED TO AT THE END OW
;	PROCESSING, AND DISCONNECTS THE PHONE IF
;	'D' WAS SPECIFIED AS A SUB-OPTION.
;
CKDIS	LDA	DISCFLG 	;CHECK 'D' FLAG
	ORA	A		;REQUESTED?
;
	IF	PMMI OR DCH
	JNZ	NDIS		;..NO, JUST EXIT
	ENDIF
;
	IF	NOT PMMI AND NOT DCH
	JNZ	EXIT
	ENDIF
;
; AWAIT C/R TO DISC. SO WE DON'T LOSE THE PHONE
;
	CALL	ILPRT
	DB	CR,LF,'Press RETURN to disconnect:',0
	CALL	KEYIN
	PUSH	PSW
	CALL	CRLF
	POP	PSW
	CPI	CR
	JNZ	CKDIS		;ASK AGAIN
;
;---->	DISCONN: DISCONNECT THE PHONE
;
DISCONN:LDA	CPTRFLG
	ORA	A
	CNZ	CPTRTOG	;IF CAPTURE WAS ON, TURN OFF
;
	IF	PMMI
	XRA	A		;GET DISCONN VALUE
	CALL	OUTCTL		;RESET ORIG/ANSW
	CALL	OUTCT2		;TURN OFF DTR, DO BREAK
	ENDIF
;
	IF	DCH
	XRA	A		;GET DISCONNECT VALUE
	CALL	OUTCTL		;DISCONNECT
	ENDIF
;
	CALL	ILPRT		;PRINT:
	DB	'++Disconnected++',0
	JMP	EXIT
;
; NO DISCONNECT, TYPE MSG AS REMINDER THAT PHONE'S
; OFF HOOK
;
	IF	PMMI OR DCH
NDIS	LDA	QFLG		;QUIET..
	ORA	A		;..MODE?
	JZ	EXIT		;..YES, NO MSG
	CALL	ILPRT
	DB	CR,LF,'++The MODEM is still connected++',CR,LF
	DB	'Use "MODEM D" to disconnect',CR,LF,0
	JMP	EXIT
	ENDIF			;PMMI OR DCH
;
;---->	INITMOD: INITIALIZES THE MODEM
;
;	THIS ROUTINE IS USED TO INITIALIZE SERIAL
;	BOARDS, OR SETUP S-100 MODEM BOARDS.
;	JUST RETURNS IF NO INITIALIZATION REQUIRED.
;
INITMD:
	IF	INITREQ AND (NOT DCH) AND (NOT X8250)
	MVI	A,INITC1	;GET 1ST INIT CHAR
	CALL	OUTCTL		;OUTPUT IT
	NOP
	NOP			;DELAY FOR USART
	NOP
	NOP
	MVI	A,INITC2	;GET 2ND INIT CHAR
	CALL	OUTCTL		;OUTPUT IT
	NOP
	NOP			;DELAY FOR USART
	NOP
	NOP
	MVI	A,INITC3	;GET 3RD INIT CHAR
	CALL	OUTCTL		;OUTPUT IT
	NOP
	NOP			;DELAY FOR USART
	NOP
	NOP
	MVI	A,INITC4	;GET 4TH INIT CHAR
	CALL	OUTCTL		;OUTPUT IT
	ENDIF			;INITREQ AND (NOT DCH) AND (NOT X8250)
;
	IF	DCH
	CALL	GETBAUD 	;GET BAUD RATE
	ENDIF
;
	IF	PMMI
	CALL	GETBAUD 	;GET BAUD RATE
	CALL	OUTBRP		;OUT BAUD RATE PORT
;
; SET MODEM CHIP BIT FOR >300 BAUD IF REQ'D
;
	CPI	52		;>300?
	MVI	A,5FH		;VALUE FOR >300
	JC	GT300
	MVI	A,7FH		;VALUE FOR <=300
GT300	CALL	OUTCT2		;SET IT
;
; SET ORIG/ANSW IF REQUESTED
;
	LDA	ORIGFLG
	ORA	A		;ORIG MODE?
	MVI	A,ORIGMD
	JZ	OFFHOOK 	;YES
	LDA	ANSWFLG
	ORA	A		;ANSW MODE?
	MVI	A,ANSWMD
	RNZ			;NO
	ENDIF			;PMMI
;
	IF	DCH
	LDA	ANSWFLG
	ORA	A		;ANSW MODE?
	MVI	B,ANSWMD
	JZ	INITM1		;YES
	LDA	ORIGFLG
	ORA	A		;ORIG MODE?
	MVI	B,ORIGMD
	JZ	INITM1		;YES
	LDA	HOLDD		;NEITHER - GET LAST VALUE
	MOV	B,A		;STORE IN B
;
INITM1: MOV	A,B		;GET MODE
	STA	HOLDD		;SAVE VALUE
	MOV	A,C		;GET BAUD RATE INDICATOR
	ORA	A		;ZERO IF 110 BAUD
	MOV	A,B		;GET MODE
	JZ	OFFHOOK 	;DO OFFHOOK
	ORI	1		;SET 300 BAUD
	ENDIF			;DCH
;
	IF	PMMI OR DCH
;
; GO OFFHOOK IN REQUESTED (ORIG/ANSW) MODE
;
OFFHOOK LXI	H,4000		;DELAY AMT
;
OFFDLY	DCR	L
	JNZ	OFFDLY
	DCR	H
	JNZ	OFFDLY
	CALL	OUTCTL		;GO OFF HOOK
	RET
	ENDIF			;PMMI OR DCH
;
	IF	X8250
	MVI	A,INITC1	;ACCESS DIVISOR LATCHES
	CALL	OUTCTL3
	MVI	A,INITC2	;SET DTR AND RTS
	CALL	OUTCTL4
	MVI	A,INITC3	;BAUD DIVISOR LOW BYTE
	CALL	OUTDATA
	MVI	A,INITC4	;BAUD DIVISOR HIGH BYTE
	CALL	OUTCTL1
	MVI	A,INITC5	;DISABLE DIVISOR LATCHES,
	CALL	OUTCTL3 	;  SET 8 BIT WORD, 1 STOP, NO PAR
	MVI	A,INITC6	;DISABLE INTERRUPTS FROM 8250
	CALL	OUTCTL1
	ENDIF			;X8250
;
;---->	GETBAUD: GETS BAUD RATE FROM COMMAND
;
;	THIS ROUTINE CHECKS IF A BAUD RATE HAS
;	BEEN ASKED FOR, (SUCH AS MODEM T.450),
;	AND IF SO, CALCULATES THE PMMI BAUD RATE
;	VALUE TO BE OUTPUT.  DEFAULTS TO 300.
;
	IF	PMMI
GETBAUD LDA	FCB+9		;GET 'FILETYPE'
	CPI	' '		;DEFAULT?
	MVI	A,DEFBAUD	;DEFAULT BAUD RATE
	RZ			;NO BAUD RATE, USE 300
;
; GOT BAUD RATE - CONVERT TO PROPER TIMER VALUE
;
	CALL	CVBIN	;CONVERT NUMBER TO BINARY
;
; CALCULATE THE VALUE TO OUTPUT:
;   RATE = 250000/16/BAUD RATE
;   DIVIDE BY USING REPETITIVE SUBTRACTION
;
; COMPLEMENT THE BAUD RATE
;
	MOV	A,H		;GET HI
	CMA			;COMPLEMENT
	MOV	D,A		;SAVE
	MOV	A,L		;GET LO
	CMA			;COMPLEMENT
	MOV	E,A		;SAVE
	INX	D		;DE=2'S COMPLEMENT
; DIVIDE
	LXI	H,15625 	;250000/16
	LXI	B,-1		;INIT QUOTIENT
;
DIVLP	INX	B		;BUMP QUOTIENT
	DAD	D		;'SUBTRACT'
	JC	DIVLP		;LOOP TIL DOOE
; VALIDATE THE RESULT
	MOV	A,B		;CAN'T HAVE >255
	ORA	A
	MOV	A,C		;GET ACTUAL
	RZ			;RET IF <256
	JMP	BADRATE 	;INVALID
	ENDIF			;PMMI
;
	IF	DCH
GETBAUD LDA	FCB+9		;GET 'FILETYPE'
	CPI	' '		;DEFAULT?
	JNZ	GETBAU1 	;NO - DO BAUD RATE STUFF
	MVI	C,1		;SET 300 BAUD
	MVI	B,17H		;SET 1 STOP BIT
	JMP	GETBAU2
;
GETBAU1 CALL	CVBIN		;CONVERT TO BINARY
	PUSH	H		;SAVE BAUD RATE
	MVI	C,0		;ANTICIPATE 110 BAUD
	MVI	B,1FH		;SET 2 STOP BITS
	LXI	D,-110		;GET CONSTANT
	DAD	D		;SUBTRACT
	MOV	A,H
	ORA	L
	POP	H
	JZ	GETBAU2 	;110 BAUD
	MVI	B,17H		;SET 1 STOP BIT
	INR	C
	LXI	D,-300		;GET CONSTANT
	DAD	D
	MOV	A,H
	ORA	L
	JNZ	BADRATE 	;INVALID
;
GETBAU2 MOV	A,B		;GET SET UP
	CALL	OUTCT2		;INIT STOP & DATA BITS, ETC
	RET
	ENDIF			;DCH
;
; ROUTINE TO CONVERT BAUD RATE TO BINARY
;
	IF	PMMI OR DCH
CVBIN:	LXI	D,FCB+9 	;TO ASCII VALUE
	LXI	H,0		;INIT BINARY RESULT
;
DECLP	LDAX	D		;GET ASCII DIGIT
	INX	D		;TO NEXT DIGIT
	CPI	' '		;BLANK ONE?
	JZ	DECLP		;..YES, SKIP IT
	CPI	'0'		;VAMIDATE IT
	JC	BADRATE 	;ERROR
	CPI	'9'+1		;VALIDATE
	JNC	BADRATE 	;ERROR
	SUI	'0'		;MAKE DIGIT BINARY
;
; MULTIPLY PREV VALUE BY 10
;
	MOV	B,H		;SET UP FOR
	MOV	C,L		;MULTIPLY BY 10
	DAD	H		;MULTIPLY BY 2
	DAD	H		;X 2 = 4
	DAD	B		;+ 1 = 5
	DAD	H		;X 2 = 10
	ADD	L		;ADD IN DIGIT
	MOV	L,A		;SAVE BACK
	JNZ	DIGNC		;NO CARRY?
	INR	H		;ADD IN CARRY
;
; CHECK IF DONE
;
DIGNC	MOV	A,E		;SEE IF PAST
	CPI	FCB+12		;..LAST DIGIT
	JNZ	DECLP		;NO, LOOP
	RET
;
; INVALID BAUD RATE
;
BADRATE CALL	ERXIT
	DB	'++Invalid baud rate++',CR,LF,'$'
	ENDIF			;PMMI OR DCH
;
; THE FOLLOWING PROVIDES A RETURN FROM INITMOD
;
	IF	(NOT PMMI) AND (NOT DCH)
	RET			;**THIS MUST BE HERE**
	ENDIF			;(NOT PMMI) AND (NOT DCH)
;
;---->	MOVEFCB: MOVES FCB(2) TO FCB
;
;	I ATTEMPTED TO MAKE THE MODEM COMMAND 'NATURAL',
;	I.E. MODEM SEND FILENAME (MODEM S FN.FT) RATHER
;	THAN MODEM FILENAME SEND (MODEM FN.FT S) SO THIS
;	ROUTINE MOVES THE FILENAME FROM THE SECOND FCB
;	TO THE FIRST
;
MOVEFCB LXI	H,FCB+16	;FROM
	LXI	D,FCB		;TO
	MVI	B,16		;LEN
	CALL	MOVE		;DO THE MOVE
	XRA	A		;GET 0
	STA	FCBSNO		;ZERO SECTOR #
	STA	FCBEXT		;..AND EXTENT
	RET
;
;---->	SHOW: SHOWS CHAR SENT/RECEIVED
;
;	CR, LF, AND TAB ARE SHOWN.  ALL OTHER
;	NON-PRINTABLE CHARACTERS ARE SHOWN IN
;	HEX AS (XX)
;
SHOW	CPI	LF		;LF?
	JZ	CTYPE		;..YES, TYPE IT
	CPI	CR		;CR?
	JZ	CTYPE		;..YES, TYPE IT
	CPI	09		;TAB
	JZ	CTYPE		;..YES, TYPE IT
	CPI	' '		;CTL-CHR?
	JC	SHOWHEX 	;YES, SHOW IN HEX
	CPI	7FH		;DEL?
	JC	CTYPE		;NO, TYPE THE CHAR
;
SHOWHEX PUSH	PSW		;SAVE THE CHAR
	MVI	A,'('		;TYPE..
	CALL	CTYPE		;..'('
	POP	PSW		;THEN..
	CALL	HEXO		;..THE CHAR
	MVI	A,')'		;THEN..
	JMP	CTYPE		;..')' AND RETURN.
;
;---->	CTYPE: TYPES VIA CP/M SO TABS ARE EXPANDED
;
CTYPE	PUSH	B		;SAVE..
	PUSH	D		;..ALL..
	PUSH	H		;..REGS
	MOV	E,A		;CHAR TO E
	MVI	C,WRCON 	;GET BDOS FNC
	CALL	BDOS		;PRIN THE CHR
	POP	H		;RESTORE..
	POP	D		;..ALL..
	POP	B		;..REGS
	RET			;FROM "CTYPE"
;
CRLF	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
;
;---->	TYPE: TYPE VIA DIRECT CBIOS ACCESS.
;	WE ASSUME CBIOS MAY DESTROY SOME REGISTERS,
;	SO SAVE THEM ALL.
;
;	THIS ROUTINE BYPASSES CP/M'S CTL-S, CTL-C
;	TESTS.
;
TYPE	PUSH	PSW		;SAVE CHAR
	PUSH	B		;AND OTHER REGISTERS
	PUSH	D
	PUSH	H
	MOV	C,A		;FOR BIOS
VTYPE	CALL	$-$		;MODIFIED AT INIT
	POP	H		;RESTORE REGISTERS
	POP	D
	POP	B
	POP	PSW		;..AND CHAR
	RET			;FROM "TYPE"
;
;---->	STAT: KEYBOARD STATUS
;
;	SAVE ALL REGISTERS, EXCEPT A, IN CASE
;	CBIOS CLOBBERS THEM.
;
STAT	PUSH	B
	PUSH	D
	PUSH	H
VSTAT	CALL	$-$		;ADDR SET AT INIT
	POP	H
	POP	D
	POP	B
	ORA	A		;0 => NOT READY
	RET
;
;---->	KEYIN: KEYBOARD INPUT
;
;	SAVE ALL REGISTERS, EXCEPT A, IN CASE
;	CBIOS CLOBBERS THEM.
;
KEYIN	PUSH	B
	PUSH	D
	PUSH	H
VKEYIN	CALL	$-$		;ADDR SET AT INIT
	POP	H
	POP	D
	POP	B
	ANI	7FH		;STRIP PARITY IF THERE
	RET			;FROM KEYIN
;
;---->	HEXO: HEX OUTPUT
;
HEXO	PUSH	PSW		;SAVE FOR RIGHT DIGIT
	RAR			;RIGHT..
	RAR			;..JUSTIFY..
	RAR			;..LEFT..
	RAR			;..DIGIT..
	CALL	NIBBL		;PRINT LEFT DIGIT
	POP	PSW		;RESTORE RIGHT
;
NIBBL	ANI	0FH		;ISOLATE DIGIT
	CPI	10		;IS IS <10?
	JC	ISNUM		;YES, NOT ALPHA
	ADI	7		;ADD ALPHA BIAS
;
ISNUM	ADI	'0'		;MAKE PRINTABLE
	JMP	TYPE		;..THEN TYPE IT
;
;---->	CKQUIT: QUIT/RETRY AFTER X8251 MULTIPLE ERRORS
;
;	RETURNS W/ZERO SET IF "RETRY" ASKED FOR
;
CKQUIT	XRA	A		;ZERO..
	STA	ERRCT		;..ERROR COUNT
	CALL	ILPRT		;PRINT:
	DB	BELL,'Multiple errors encountered.  '
	DB	'Type ''Q'' to quit, ''R'' to retry: ',0
	CALL	KEYIN		;QUIT/RETRY
	PUSH	PSW
	CALL	TYPE
	CALL	CRLF
	POP	PSW
	ANI	5FH		;MAKE UPPER CASE
	CPI	'R'		;RETRY?
	RZ			;'KEEP ON TRUCKIN'
	CPI	'Q'		;QUIT?
	JNZ	CKQUIT		;NO, ASK AGAIN
	ORA	A		;SET NON-ZERO
	RET
;
;---->	ILPRT: INLINE PRINT OF MSG
;
;	THE CALL TO ILPRT IS FOLLOWED BY A MESSAGE,
;	BINARY 0 AS THE END.  BINARY 1 MAY BE USED TO
;	PAUSE (MESSAGE 'PRESS RETURN TO CONTINUE')
;
ILPRT	XTHL			;SAVE HL, GET HL=MSG
;
ILPLP	MOV	A,M		;GET CHAR
	ORA	A		;END OF MSG?
	JZ	ILPRET		;..YES, RETURN
	CPI	1		;PAUSE WITH MSG?
	JZ	ILPAUSE 	;..YES
	CPI	2		;PAUSE, NO MSG?
	JZ	ILPAUSE1	;..YES
	CALL	CTYPE		;TYPE THE MSG
;
ILPNEXT INX	H		;TO NEXT CHAR
	JMP	ILPLP		;LOOP
;
; PAUSE WHILE TYPING HELP SO INFO DOESN'T
; SCROLL OFF OF VIDEO SCREENS
;
ILPAUSE CALL	ILPRT		;PRINT:
	DB	CR,LF,'Press RETURN to continue: ',0
ILPAUSE1 CALL	KEYIN		;GET ANY CHAR
	CPI	'C'-40H 	;REBOOT?
	JZ	EXIT		;YES.
	CALL	ILPRT		;PRINT
	DB	CR,LF,0
	JMP	ILPNEXT 	;LOOP
;
ILPRET	XTHL			;RESTORE HL
	RET			;PAST MSG
;
;---->	PRTMSG: PRINTS MSG POINTED TO BY (DE)
;
;	A '$' IS THE ENDING DELIMITER FOR THE PRINT.
;	NO REGISTERS SAVED.
;
PRTMSG	MVI	C,PRINT 	;GET BDOS FNC
	JMP	BDOS		;PRINT MESSAGE, RETURN
;
;---->	ERXIT: EXIT PRINTING MSG FOLLOWING CALL
;
ERXIT	POP	D		;GET MESSAGE
	CALL	PRTMSG		;PRINT IT
	CALL	CKDIS		;DISCONNECT?
;
EXIT	CALL	ILPRT		;PRINT:
	DB	CR,LF,0
	LHLD	STACK		;GET ORIGINAL STACK
	SPHL			;RESTORE IT
	RET			;--EXIT-- TO CP/M
;
; MOVE 128 CHARACTERS
;
MOVE128 MVI	B,128		;SET MOVE COUNT
;
; MOVE FROM (HL) TO (DE) LENGTH IN (B)
;
MOVE	MOV	A,M		;GET A CHAR
	STAX	D		;STORE IT
	INX	H		;TO NEXT "FROM"
	INX	D		;TO NEXT "TO"
	DCR	B		;MORE?
	JNZ	MOVE		;..YES, LOOP
	RET			;..NO, RETURN
;************************************************************************
;* CRCSUBS (Cyclic Redundancy Code Subroutines) version 1.20		*
;* 8080 Mnemonics							*
;*									*
;*	These subroutines will compute and check a true 16-bit		*
;*	Cyclic Redundancy Code for a message of arbitrary length.	*
;*									*
;*	The  use  of this scheme will guarantee detection of all	*
;*	single and double bit errors, all  errors  with  an  odd	*
;*	number	of  error bits, all burst errors of length 16 or	*
;*	less, 99.9969% of all 17-bit error bursts, and	99.9984%	*
;*	of  all  possible  longer  error bursts.  (Ref: Computer	*
;*	Networks, Andrew S.  Tanenbaum, Prentiss-Hall, 1981)		*
;*									*
;*									*
;*	There are four entry points, which are used as follows: 	*
;*									*
;*	CLRCRC - A call to this entry resets the CRC accumulator.	*
;*		 It must be called at the start of each message.	*
;*									*
;*		 Entry Parameters: None.				*
;*									*
;*		 Exit Conditions:  CRC accumulator cleared.		*
;*				   All registers preserved.		*
;*									*
;*									*
;*	UPDCRC - A call to this entry updates the CRC accumulator.	*
;*		 It must be called once for each byte in the		*
;*		 message for which the CRC is being calculated. 	*
;*									*
;*		 Entry Parameters: (A) = a byte to be included		*
;*					 in the CRC calculation.	*
;*									*
;*		 Exit Conditions:  CRC accumulator updated.		*
;*				   All registers preserved.		*
;*									*
;*									*
;*	FINCRC - A call to this entry finishes the CRC calculation	*
;*		 for a message which is to be TRANSMITTED. It must	*
;*		 be called after the last byte of the message has	*
;*		 been passed thru UPDCRC. It returns the calculated	*
;*		 CRC bytes, which must be transmitted as the final	*
;*		 two bytes of the message (first D, then E).		*
;*									*
;*		 Entry Parameters: None.				*
;*									*
;*		 Exit Conditions:  (DE) = calculated CRC bytes. 	*
;*				   All other registers preserved.	*
;*									*
;*									*
;*	CHKCRC - A call to this routine checks the CRC bytes of 	*
;*		 a RECEIVED message and returns a code to indicate	*
;*		 whether the message was received correctly. It must	*
;*		 be called after the message AND the two CRC bytes	*
;*		 have been received AND passed thru UPDCRC.		*
;*									*
;*		 Entry Parameters: None.				*
;*									*
;*		 Exit Conditions:  (A) =  0 if message ok.		*
;*				   (A) = -1 if message garbled. 	*
;*				   All other registers preserved.	*
;*									*
;************************************************************************
;*									*
;*	Designed & coded by Paul Hansknecht, June 13, 1981		*
;*									*
;*									*
;*	Copyright (c) 1981, Carpenter Associates			*
;*			    Box 451					*
;*			    Bloomfield Hills, MI 48013			*
;*			    313/855-3074				*
;*									*
;*	This program may be freely reproduced for non-profit use.	*
;*									*
;************************************************************************
;
CLRCRC: EQU	$		; Reset CRC Accumulator for a new message.
	PUSH	H
	LXI	H,0
	SHLD	CRCVAL
	POP	H
	RET
;
UPDCRC: EQU	$		; Update CRC Accumulator using byte in (A).
	PUSH	PSW
	PUSH	B
	PUSH	H
	MVI	B,8
	MOV	C,A
	LHLD	CRCVAL
;
UPDLOOP:MOV	A,C
	RLC
	MOV	C,A
	MOV	A,L
	RAL
	MOV	L,A
	MOV	A,H
	RAL
	MOV	H,A
	JNC	SKIPIT
	MOV	A,H		; The generator is X^16 + X^12 + X^5 + 1
	XRI	10H		; as recommended by CCITT.
	MOV	H,A		; An alternate generator which is often
	MOV	A,L		; used in synchronous transmission protocols
	XRI	21H		; is X^16 + X^15 + X^2 + 1. This may be
	MOV	L,A		; used by substituting XOR 80H for XOR 10H
SKIPIT: DCR	B		; and XOR 05H for XOR 21H in the adjacent code.
	JNZ	UPDLOOP
	SHLD	CRCVAL
	POP	H
	POP	B
	POP	PSW
	RET
;
FINCRC: EQU	$		; Finish CRC calc for outbound message.
	PUSH	PSW
	XRA	A
	CALL	UPDCRC
	CALL	UPDCRC
	PUSH	H
	LHLD	CRCVAL
	MOV	D,H
	MOV	E,L
	POP	H
	POP	PSW
	RET
;
CHKCRC: EQU	$		; Check CRC bytes of received message.
	PUSH	H
	LHLD	CRCVAL
	MOV	A,H
	ORA	L
	POP	H
	RZ
	MVI	A,0FFH
	RET
;
CRCVAL	DW	0	;CRC VALUE STORAGE
;
OPTION	DB	0	;PRIMARY OPTION
;
; DATAFLG IS USED BY THE "V" SUBCOMMAND -
; IT IS 0 WHEN A HEADER OR CKSUM IS BEING
; SENT/RCD, AND 1 IF "VIEWABLE" DATA (THE
; SECTOR ITSELF)
;
DATAFLG DB	0	;AT HEADER, FIRST
;
; SUB-OPTION TABLE.  IF AN OPTION IS IN EFFECT,
; THE CHARACTER IS SET TO BINARY 0
;
OPTBL	EQU	$
ANSWFLG DB	'A'		;ANSWER MODE
DISCFLG DB	'D'		;DISCONNECT WHEN DONE
ECHOFLG DB	'E'		;TO ECHO AFTER XFER
ORIGFLG DB	'O'		;ORIGINATE MODE
QFLG	DB	'Q'		;QUIET TRANSFER (NO MSGS)
RSEEFLG DB	'R'		;SEE WHAT'S RECEIVED
SSEEFLG DB	'S'		;SEE WHAT'S SENT
TERMFLG DB	'T'		;TO TERM AFTER XFER
VSEEFLG DB	'V'		;VIEW MESSAGES (NO HDR, ETC)
CRCFLG	DB	'C'		;USE CRC INSTEAD OF CHECKSUM
OPTBE	EQU	$		;END OF OPTIONS
;
FIRST	DB	0FFH		;FLAG FOR FIRST TIME THRU SND OR RCV
FIRSTME DB	0FFH		;FLAG FOR FIRST SOH RCVD IN CRC MODE
;
MAXEXT	DB	0		;EXT COUNT USED BY 'CNREC'
RCNT	DW	0		;EXTENT RECORD COUNT USED BY 'CNREC'
RCVSNO	DB	0		;SECT # RECEIVED
SECTNO:	DW	0		;CURRENT SECTOR NUMBER (LO, THEN HI)
ERRCT	DB	0		;ERROR COUNT
ERRCDE	DB	0		;RECEIVE ERROR CODE
HOLDD	DB	86H		;DC HAYES MODEM DEFAULT HOLDING AREA
;
; FOLLOWING USED BY ASCII CAPTURE AND PRINTER FUNCTIONS
;
CPTRFLG DB	0		;CAPTURE ENABLED FLAG
PRTRFLG DB	0		;PRINTER ENABLED FLAG
ASCFCB	DS	33		;CAPTURE FCB
MEMEND	DS	1		;LAST PAGE OF TPA
CAPPTR	DS	2		;BUFFER POINTER
;
; FOLLOWING 3 USED BY DISK BUFFERING ROUTINES
;
EOFLG	DB	0		;EOF FLAG (1=TRUE)
SECPTR	DW	DBUF
SECINBF DB	0		;# OF SECTORS IN BUFFER
;
	DB	055H,0AAH	;GUARD BYTES TO HELP
				; IDENTIFY STACK OVERFLOWS,
				; NOT USED BY PROGRAM, ONLY
				; FOR DUMP ANALYSIS
	DS	128		;STACK AREA
STACK	DS	2		;STACK POINTER
;
; DISK BUFFER (OVERLAYS HELP MSGS), SIZE=128*DBUFSIZ*8.
; NO PROGRAM CHECK IS DONE TO VERIFY THAT THE BUFFER
; WILL NOT OVERLAY THE CCP AND/OR BDOS ON A GIVEN SYSTEM,
; SO THE BURDEN IS ON THE USER TO MAKE SURE OF THIS.
; A REASONABLE BUFFER SIZE IS 16K. TRADITIONALLY, BUFFER
; SIZE HAS BEEN 2K.
;
DBUF	EQU	$
;
;  ASCII CAPTURE BUFFER, MUST START ON PAGE BOUNDARY
;
ASCBUF:	EQU	DBUF+0FFH AND 0FF00H	;USE SAME DISK BUFFER FOR ASCII
				;CAPTURE, SINCE CAPTURE NOT DONE
	;WHILE XFER IN PROGRESS. TO HAVE SEPARATE BUFFERS LIKE IN
	;MODEM220:	ASCBUF	EQU	(DBUF+(DBUFSIZ*1024)+0FFH) AND 0FF00H
;
; INVALID COMMAND
;
BADOPT	PUSH	PSW		;SAVE BAD OPTION
	CALL	CRLF
	CALL	ILPRT
	DB	'++''',0
	POP	PSW	;RETRIEVE BAD OPTION
	CALL	TYPE	;PRINT BAD OPTION
	CALL	ILPRT	;EXIT W/ERROR
	DB	''' is an invalid MODEM command option++',CR,LF,CR,LF
	DB	'Press RETURN for help, Ctl-C to exit: ',2,0
;
HELP	CALL	ILPRT
	DB	'(T)erminal and (E)cho mode commands:',CR,LF
	DB	'   Ctl-Y = Ascii capture enable/disable toggle',CR,LF
	DB	'   Ctl-E = Exit to CP/M',CR,LF
	DB	'   Ctl-D = Disconnect phone',CR,LF
	DB	'   Ctl-P = Printer enable/disable toggle',CR,LF,CR,LF
	DB	'Format for command is:',CR,LF,CR,LF
	DB	'MODEM # Filename',CR,LF,CR,LF
	DB	'Where # is a 1 character primary option,',CR,LF
	DB	' which may be followed by sub-options,',CR,LF
	DB	' and by ".xxx" to set baud rate to xxx',CR,LF,CR,LF,1
	DB	'Primary Options:',CR,LF,CR,LF
	DB	'   S to send a file',CR,LF
	DB	'   R to receive a file',CR,LF
	DB	'   T to act as a terminal',CR,LF
	DB	'   E to act as a computer (echo data)',CR,LF
	DB	'   D to disconnect the phone',CR,LF
	DB	'   H to print this help file',CR,LF,CR,LF,1
	DB	'Secondary options:',CR,LF
	DB	'   A answer mode',CR,LF
	DB	'   O originate mode',CR,LF
	DB	'   D disconnect after execution',CR,LF
	DB	'   T go to terminal mode after file xfer',CR,LF
	DB	'   E go to echo mode after file xfer',CR,LF
	DB	'   Q quiet mode - no status msgs',CR,LF
	DB	'   R show chars received',CR,LF
	DB	'   S show chars sent',CR,LF
	DB	'   V view file sent/received (no status)',CR,LF
	DB	'   C use cyclic redundancy check for file xfers',CR,LF
	DB	CR,LF,'For examples, type: MODEM X',CR,LF,0
	JMP	EXIT
;
EXAM	CALL	ILPRT
	DB	'Send file, originate mode, 300 baud:',CR,LF
	DB	'   MODEM SO fn.ft',CR,LF
	DB	'Send another file:',CR,LF
	DB	'   MODEM S fn/ft',CR,LF
	DB	'Then send a third file at 450 baud and disconnect:'
	DB	CR,LF,'     MODEM SD.450 fn.ft',CR,LF
	DB	'Act as a terminal, originate mode, at 110 baud:',CR,LF
	DB	'   MODEM TO.110',CR,LF
	DB	'   (Use ctl-D to disconnect)',CR,LF
	DB	'Receive file, answer mode, view it, 600 baud:',CR,LF
	DB	'   MODEM RAV.600 fn.ft',CR,LF
	DB	'Receive file, use cyclic redundancy check, 600 baud:',CR,LF
	DB	'   MODEM RC.600 fn,ft',CR,LF
	DB	'Turn printer mode on/off in terminal or echo mode:',CR,LF
	DB	'   ^P	 (printer now toggled)',CR,LF
	DB	'Turn on ascii capture mode:',CR,LF
	DB	'   ^Y ',CR,LF
	DB	'   FILE NAME --> b:test.doc [CR] (file now open)',CR,LF
	DB	'Turn off ascii capture mode:',CR,LF
	DB	'   ^Y	(ascii capture disabled, file closed)',CR,LF,0

	JMP	EXIT
;
; BDOS EQUATES (VERSION 2)
;
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
CONST	EQU	11		;CONSOLE STAT
OPEN	EQU	15		;0FFH=NOT FOUND
CLOSE	EQU	16		;	"	"
SRCHF	EQU	17		;	"	"
SRCHN	EQU	18		;	"	"
ERASE	EQU	19		;NO RET CODE
READ	EQU	20		;0=OK- 1=EOF
WRITE	EQU	21		;0=OK, 1=ERR, 2=?, 0FFH=NO DIR SPC
MAKE	EQU	22		;0FFH=BAD
REN	EQU	23		;0FFH=BAD
STDMA	EQU	26		;SET DMA
BDOS	EQU	BASE+5
REIPL	EQU	BASE
FCB	EQU	BASE+5CH	;SYSTEM FCB
FCBEXT	EQU	FCB+12		;FILE EXTENT
FCBSNO	EQU	FCB+32		;SECTOR #
FCB2	EQU	FCB+6CH 	;2ND FCB
	END
