;
;MODEM.ASM V2.0, BY WARD CHRISTENSEN
;	(revised 12/6/79)
;
;CP/M - CP/M FILE TRANSFER PROGRAM, AND
;TERMINAL PROGRAM.
;
;NOTE: THIS FILE WILL ASSEMBLE, WITHOUT NEED FOR
;EDITING, TO WORK WITH A PMMI MM-103 MODEM.  SEE
;EQUATES FOR OTHER OPTIONS INCLUDING SYSTEM CLOCK
;FREQUENCY.
;
* * * * * * * * * * * * * * * * * * * * * * * * *
*						*
*    THIS PROGRAM DOCUMENTED IN "MODEM.DOC"	*
*						*
* * * * * * * * * * * * * * * * * * * * * * * * *
* THIS PROGRAM WAS "MODEM.ASM" BUT IS 		*
* TEMPORARILY NAMED "MODEM2.ASM" SO PEOPLE	*
* WILL REALIZE IT IS AN ENHANCEMENT OF		*
* THE ORIGINAL PROGRAM "MODEM.ASM" ON CP/M	*
* USER'S GROUP DISK 25.				*
* * * * * * * * * * * * * * * * * * * * * * * * *
;
;PLEASE PASS ON MODS, BUGS, ETC, SO YOUR
;FIXES OR ENHANCEMENTS MAY BE SHARED BY ALL,
;	     TO:
;	Ward Christensen
;	688 E. 154th St. #5D
;	Dolton, Il. 60419
;
;	(312) 849-6279
;
;You may send a self-addressed stamped postcard
;to be informed of changes/bugs as they become
;known.
;
;	--------------
;
;	09/23/77
;ORIGINALLY WRITTEN BY WARD CHRISTENSEN
;
;	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.
;
;	05/09/79
;ALLOW 'T' AND 'E' SUB-OPTIONS TO GO TO TERMINAL
;OR ECHO MODEM AFTER TRANSFERRING A FILE.  (WLC)
;
;	05/22/79
;ADD FEATURE TO MAKE RECEIVE FILE ROUTINE SAY
;FILE SUCCESSFULLY OPENED, WHEN IN QUIET MODE.
;MOVE INITIAL GOBBLE GARBAGE INPUTS TO BEFORE
;COMMAND CPI'S SO ALL MODES ARE CLEARED. CHANGE
;INITIAL SEND WAIT TO 80 SECS TO ALLOW MORE TIME
;FOR RECEIVING END TO COME UP. ADD 'H' AFTER MSG
;THAT SHOWS NUMBER OF SECTORS IN EXTENT ABOUT TO
;BE SENT.  (KBP)
;
;	05/24/79
;FIX MISSING RETURN INSTRUCTION AT END OF
;INITIALIZATION ROUTINE.  (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 CALL "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).
;
;	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 AND KEYBOARD STATUS
;ROUTINES, AS SOME CBIOS ROUTINES CLOBBER THEM. (KBP)
;
;	08/05/79
;ADDED D. C. HAYES MODEM SUPPORT BY JIM BELL  (KBP)
;
;	08/06/79
;ADDED EQUATES FOR EXTERNAL MODEM (NOT S-100 PLUG-IN)
;(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)
;
;	01/02/80
;THIS NOW WILL SUPPORT THE INTEL 8251 CHIP AS A INTERFACE
;TO THE MODEM. IT WILL DO ALL SET UP AND READY OF THE
;8251 BEFORE CONTACT AND IF THE MODEM IS ALREADY CON-
;NECTED TO A STATION IT WILL ASUME THAT THE CHIP IS READY.
;	THESE MODIFACATIONS MAKE IT COMPATABLE WITH THE 
;DIGITAL GROUP COMMUNICATIONS BOARD. (BILL NADZAM)
;
;
STDCPM	EQU	1	;TRUE, IS STANDARD CP/M
H8CPM	EQU	0	;TRUE, IS HEATH H8 CP/M
;
BASE	EQU	0	;CP/M BASE ADDRESS
;
;
USART	EQU	1	;TRUE,IS 8251 CHIP AS MODEM INTF.
;
PMMI	EQU	0	;TRUE, IS PMMI
;
DCH	EQU	0	;TRUE, IS D.C. HAYES
;
;IF YOU ARE USING AN EXTERNAL MODEM (NOT S-100 PLUG-IN)
;CHANGE THESE EQUATES FOR YOUR MODEM PORT REQUIREMENTS
;
INITREQ EQU	1	;TRUE,IF MODEM PORT INIT. REQ'D
INITC1	EQU	4EH	;FIRST INIT CHAR TO CTL PORT
INITC2	EQU	27H	;2ND INIT CHAR TO CTL PORT
;
;
ERRLIM	EQU	10	;MAX ALLOWABLE ERRORS
EXITCHR	EQU	'E'-40H ;CTL-E EXIT FROM T OR C
DISCCHR	EQU	'D'-40H	;CTL-D DISCONNECTS MODEM T/C
;
FASTCLK	EQU	0	;PUT 1 HERE FOR 4 MHZ CLOCK
;
;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	0	;PUT 1 HERE TO MAKE BIT 7 HIGH
;
;
;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
CAN	EQU	18H	;CANCEL
LF	EQU	10	;LINEFEED
CR	EQU	13	;CARRIAGE RETURN
; 
	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
;
;
	CALL	START	;GO PRINT ID
	DB	'MODEM PROGRAM as of '
	DB	'01/04/80',CR,LF,'$'
; 
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	'X'	;MODEM X(AMPLES)?
	JZ	EXAM	;GIVE EXAMPLES
;
;SAVE PRIMARY OPTION, VALIDATE SECONDARY OPT.
;
	CALL	PROCOPT
;
;INIT THE MODEM OR SERIAL PORT
;
	CALL	INITMOD
;
;MOVE THE FILENAME FROM FCB 2 TO FCB 1
;
	CALL	MOVEFCB
;
;GOBBLE UP GARBAGE CHARS FROM THE LINE
;PRIOR TO RECEIVE OR SEND
;
;
;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'
	JZ	DISCONN
;
       	CPI	'S'	;SEND..
	JZ	SENDFIL	;..A FILE?
;
	CPI	'R'	;RECEIVE..
	JZ	RCVFIL	;..A FILE?
;
;INVALID OPTION
;
	JMP	BADOPT
	org	base+4000h	;modified for osbornese
* * * * * * * * * * * * * * * * * * * * *
*					*
*	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
;RUNING "MODEM E" (ECHO MODE)
;
;TYPE THE "EXITCHR" (ORIGINALLY CTL-E) TO EXIT.
;OR THE "DISCCHR" (ORIGINALLY CTL-D) TO DISCONN.
;
;A FUTURE ENHANCEMENT WILL BE TO WRITE THE
;RECEIVED DATA IN MEMORY, AND ALLOW IT TO
;BE WRITTEN TO DISK
;
TERM	CALL	STAT	;LOCAL CHAR KEYED?
	JZ	TERML	;..NO, CHECK LINE
	CALL	KEYIN	;GET CHAR
	CPI	EXITCHR	;TIME TO END?
	JZ	CKDIS	;YES, CK DISCONN
	CPI	DISCCHR	;DISCONNECT REQUEST?
	JZ	DISCONN	;YES, DO IT
	call	acsend	;send the char
;
;SEE IF CHAR FROM LINE
;
terml:	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a00h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	01h
	cpi	00h
	jz	term
	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a01h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	7fh
	CALL	TYPE	;TYPE IT
	JMP	TERM	;LOOP
;
* * * * * * * * * * * * * * * * * * * * *
*					*
*	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.
;
trmecho: di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a00h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	01h
	cpi	00h
	jnz	linechr
	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.
	di
	sta	xyz
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a01h
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	CALL	TYPE	;ECHO IT LOCALLY
	JMP	TRMECHO	;..AND LOOP
;
;GOT CHAR FROM LINE
;
linechr:di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a00h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	01h
	cpi	00h
	jz	linechr
	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a01h
	sta	xyz
	lda	2a01h
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	CALL	TYPE	;TYPE IT
	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, AND RE-
;TRANSMISSION ON ERRORS.  
;
SENDFIL	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
	CALL	SENDCKS	;SEND CKSUM
	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
;
* * * * * * * * * * * * * * * * * * * * *
*					*
*	RCVFIL: RECEIVE A FILE		*
*					*
* * * * * * * * * * * * * * * * * * * * *
;
;RECEIVES A FILE IN BLOCK FORMAT AS SENT
;BY ANOTHER PERSON DOING "MODEM S FN.FT".
;
RCVFIL	CALL	ERASFIL	;ERASE THE FILE
	CALL	MAKEFIL	;..THEN MAKE NEW
	LDA	QFLG	;SEE IF IN QUIET MODE
	ORA	A
	JNZ	RCVLP	;NOT IN QUIET MODE, SKIP MSG
	CALL	ILPRT	;PRINT:
	DB	'FILE OPEN, READY TO RECEIVE',CR,LF,0
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		*
*					*
* * * * * * * * * * * * * * * * * * * * *
;
;
;---->	RCVSECT: RECEIVE A SECTOR
;
;RETURNS WITH CARRY SET IF EOT RECEIVED.
;
RCVSECT	XRA	A	;GET 0
	STA	ERRCT	;INIT ERROR COUNT
RCVRPT	LDA	QFLG	;QUIET?
	ORA	A
	JZ	RCVSQ	;YES, NO STAT MSG.
	CALL	ILPRT	;PRINT:
	DB	'AWAITING #',0
	LDA	SECTNO	;GET SECTOR #
	INR	A	;(REAL INR LATER)
	CALL	HEXO	;PRINT IN HEX
	CALL	CRLF	;..THEN CRLF
;
RCVSQ	MVI	B,10	;10 SEC TIMEOUT
	CALL	RECV	;GET SOH/EOT
	JC	RCVSTOT	;TIMEOUT
	CPI	SOH	;GET SOH?
	JZ	RCVSOH	;..YES
;
;EARLIER VERS. 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 RCD, 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
	MVI	A,NAK	;SEND..
	CALL	SEND	;..THE NAK
	LDA	ERRCT	;ABORT IF..
	INR	A	;..WE HAVE REACHED..
	STA	ERRCT	;..THE ERROR..
	CPI	ERRLIM	;..LIMIT?
	JC	RCVRPT	;..NO, TRY AGAIN
;
;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?
	JZ	RCVSECT	;TRY AGAIN
;
RCVSABT	CALL	CLOSFIL	;KEEP WHATEVER WE GOT
	CALL	ERXIT
	DB	'++UNABLE TO RECEIVE BLOCK'
	DB	CR,LF,'++ABORTING++$'
;
;TIMEDOUT 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.
;
;GOT SOH - GET BLOCK #, BLOCK # COMPLEMENTED
;
RCVSOH	MVI	B,1	;TIMEOUT = 1 SEC
	CALL	RECV	;GET SECTOR
	JC	RCVSTOT	;GOT TIMEOUT
	MOV	D,A	;D=BLK #
	MVI	B,1	;TIMEOUT = 1 SEC
	CALL	RECV	;GET CMA'D SECT #
	JC	RCVSTOT	;TIMEOUT
	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
	LXI	H,BASE+80H	;POINT TO BUFFER
RCVCHR	MVI	B,1	;1 SEC TIMEOUT
	CALL	RECV	;GET CHAR
	JC	RCVSTOT	;TIMEOUT
	MOV	M,A	;STORE CHAR
	INR	L	;DONE?
	JNZ	RCVCHR	;NO, LOOP
;
;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
	CMP	D	;CHECKSUM OK?
	JNZ	RCVCERR	;NO, ERROR
;
;GOT A SECTOR, IT'S A DUP IF = PREV,
;	OR OK IF = 1 + PREV SECTOR
;
	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
;
;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++ ',0
	JMP	RCVPRN	;PRINT ERROR #
;
;PREV SECT 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.
	CALL	ILPRT	;PRINT:
	DB	'SEND # ',0
	LDA	SECTNO	;PRINT..
	CALL	HEXO	;..SECT #
	CALL	CRLF	;..THEN CR/LF
;
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
	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
;
;---->	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 AND 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
	ANI	7FH	;STRIP PARITY BIT
	CPI	CAN	;CANCEL TRANSMISSION?
	JZ	ABORT	;..YES
	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 RCD, 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 '
	DB	'- ABORTING',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
; 
;---->	CKABORT: CHECK FOR LOCAL ABORT
;
;IF THE USER WANTS TO CANCEL THE TRANSMISSION,
;TYPING CTL-X WILL ABORT IT.  NOTE THIS TEST
;IS NOT MADE IF IN QUIET (NO CONSOLE I/O) MODE.
;
CKABORT	LDA	VSEEFLG	;VIEWING?
	ORA	A
	JZ	CKABGO	;YES, CHECK
	LDA	QFLG	;SUPPRESSED..
	ORA	A	;..CONSOLE I/O?
	RZ		;YES, NO TEST
CKABGO	CALL	STAT	;KEY PRESSED?
	RZ		;NOTHING FROM KEYBOARD
	CALL	KEYIN	;GET DATA
	CPI	CAN	;TIME TO END?
	RNZ		;NO, CONTINUE
; 
ABORT	LXI	SP,STACK
ABORTL	MVI	B,1	;1 SEC. W/O CHARS.
	CALL	RECV
	JNC	ABORTL	;LOOP UNTIL SENDER DONE
	MVI	A,CAN	;CONTROL X
	CALL	SEND	;STOP SENDING END
ABORTW	MVI	B,1	;1 SEC W/O CHARS.
	CALL	RECV
	JNC	ABORTW	;LOOP UNTIL SENDER DONE
	MVI	A,' '	;GET A SPACE...
	CALL	SEND	;TO CLEAR OUT CONTROL X
	CALL	ILPRT	;EXIT WITH ABORT MSG
	DB	'MODEM PROGRAM CANCELLED',CR,LF,0
	JMP	CKDIS	;CHECK FOR DISCONN.
;
;---->	INCRSNO: INCREMENT SECTOR #
;
INCRSNO	LDA	SECTNO	;INCR..
	INR	A	;..SECT..
	STA	SECTNO	;..NUMBER
	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: ',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	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$'
;
OPENOK	CALL	ILPRT	;PRINT:
	DB	'FILE OPEN, EXTENT LENGTH: ',0
	LDA	FCB+15	;GET # SECTORS
	CALL	HEXO	;PRINT IN HEX
	MVI	A,'H'
	CALL	TYPE	;PRINT 'H' AFTER NUMBER
	CALL	CRLF	;..THEN CRLF
	RET
;
;---->	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	'CAN''T CLOSE FILE$'
;
;---->	RDSECT: READS A SECTOR
;
;FOR SPEED, THIS ROUTINE BUFFERS UP 16
;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 16
;
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	'++FILE READ ERROR$'
;
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	16	;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 16
;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	16	;HAVE WE 16?
	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.
	MVI	C,CAN	;CANCEL..
	CALL	SEND	;..SENDER
	CALL	ERXIT	;EXIT W/MSG:
	DB	'++ERROR WRITING FILE',CR,LF,'$'
;
;---->	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
;
RECV	PUSH	D	;SAVE
;
;
MSEC	LXI	D,50000	;1 SEC DCR COUNT
	CALL	CKABORT	;CHECK FOR EXIT REQUEST
mwti:	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a00h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	01h
	cpi	00h
	jnz	mchar
	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
;
mchar:	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	2a01h
	sta	xyz	
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	POP	D	;RESTORE DE
;
;CALC CHECKSUM
;
	PUSH	PSW	;SAVE THE CHAR
	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 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"
;
;---->	SEND: SEND A CHARACTER TO THE MODEM
;
SEND	PUSH	PSW	;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	POP	PSW	;GET THE CHAR
	PUSH	PSW	;SAVE IT
	CALL	SHOW	;SHOW IT
NOMONOT	POP	PSW	;RESTORE CHAR
	PUSH	PSW	;SAVE IT
	ADD	C	;CALC CKSUM
	MOV	C,A	;SAVE CKSUM
sendw:	di
	out	0	
	mvi	a,0
	sta	0ef08h
	lda	2a00h
	sta	xyz
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	lda	xyz
	ani	02h
	cpi	00h
	jz	sendw
	POP	PSW	;GET CHAR
acsend:	sta	xyz
	di
	out	0
	mvi	a,0
	sta	0ef08h
	lda	xyz
	sta	2a01h
	out	1
	mvi	a,1
	sta	0ef08h
	ei
	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
;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',CR,LF,0
WAITNLP	CALL	CKABORT	;ABORT IF LOCAL CTL-X
	MVI	B,1	;TIMEOUT DELAY
	CALL	RECV	;DID WE GET..
	CPI	NAK	;..A NAK?
	RZ		;YES, SEND BLOCK
	ANI	7FH	;STRIP PARITY BIT
	CPI	CAN	;CTL-X FROM REMOTE?
	JZ	ABORT	;..YES, ABORT
	DCR	E	;80 TRIES?
	JZ	ABORT	;YES, ABORT
	JMP	WAITNLP	;NO, LOOP
;
;---->	INITADR: INIT'S CP/M BDOS 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
;
;---->	PROCOPT: PROCESS COMMAND OPTIONS
; 
;1) SAVES 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 'D' IS 
;CODED (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. OPT: 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
;
;IF "VIEW" WAS ASKED FOR, SET QUIET FLAG
;
ENDOPT	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	CR,LF,'TRANSFER COMPLETE' 
	DB	CR,LF,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 OF
;PROCESSING, AND DISCONNECTS THE PHONE IF
;'D' WAS SPECIFIED AS A SUB-OPTION.
;
CKDIS	LDA	DISCFLG	;CHECK 'D' FLAG
disconn:	nop
	JMP	EXIT
;
;NO DISCONNECT, TYPE MSG AS REMINDER THAT PHONE'S
;OFF HOOK
;
;
;---->	INITMOD: INITIALIZES THE MODEM
;
;THIS ROUTINE IS USED TO INITIALIZE SERIAL
;BOARDS, OR SETUP S-100 MODEM BOARDS.
;JUST RETURNS IF NO INITIALIZATION REQUIRED.
;
INITMOD:
;
;
	RET		;**THIS MUST BE HERE**
;
;---->	MOVEFCB: MOVES FCB(2) TO FCB
;
;I ATTEMPTED TO MAKE THE MODEM COMMAND 'NATURAL',
;I.E. MODEM SEND FILENAME (MODEM S FN.FT) RATHER
;THAT 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 MULTIPLE ERRS.
;
;RETURNS W/ ZERO SET IF "RETRY" ASKED FOR
;
CKQUIT	XRA	A	;ZERO..
	STA	ERRCT	;..ERROR COUNT
	CALL	ILPRT	;PRINT:
	DB	'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?
	JZ	ILPAUSE	;..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'
	DB	CR,LF,0
	CALL	KEYIN	;GET ANY CHAR
	CPI	'C'-40H	;REBOOT?
	JZ	EXIT	;YES.
	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	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
;	----------------
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) IS
;
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)
OPTBE	EQU	$	;END OF OPTIONS
;
RCVSNO	DB	0	;SECT # RECEIVED
SECTNO	DB	0	;CURRENT SECTOR NUMBER 
ERRCT	DB	0	;ERROR COUNT
HOLDD	DB	86H	;HOLD AREA - LAST DC HAYES CONT CHAR.
;FOLLOWING 3 USED BY DISK BUFFERING ROUTINES
xyz	db	0
EOFLG	DB	0	;EOF FLAG (1=TRUE)
SECPTR	DW	DBUF
SECINBF	DB	0	;# OF SECTORS IN BUFFER
	DS	60	;STACK AREA
STACK	DS	2	;STACK POINTER
;
;16 SECTOR DISK BUFFER (OVERLAYS HELP MSGS)
;
DBUF	EQU	$	;16 SECTOR DISK BUFFER
;
;INVALID COMMAND
;
BADOPT	CALL	TYPE
	CALL	ILPRT	;EXIT W/ERROR
	DB	': INVALID OPTION ON MODEM '
	DB	'COMMAND - ',CR,LF
 DB 'PRESS RETURN FOR HELP, CTL-C IF NOT',CR,LF,1,0
;
HELP	CALL	ILPRT
 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'
 DB cr,lf,cr,lf,1
 DB 'Primary Options:',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 '	H to print this help file'
 DB cr,lf,cr,lf,1
 DB 'Secondary options:',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 CR,LF,'FOR EXAMPLES, TYPE: MODEM X',cr,lf,0
	JMP	EXIT
;
EXAM	CALL	ILPRT
 DB 'Send another file:',CR,LF
 DB '	MODEM S fn.ft',cr,lf
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	BASE+6CH	;SECOND FCB
	END

