	TITLE	'CP/M MODEM PROGRAM Version 7.65'
;
;THE FOLLOWING IS AN EXTENSIVE REVISION OF THE CP/M MODEM PROGRAM
;CREATED BY WARD CHRISTENSEN FOR THE CP/M USERS LIBRARY.
;IT ALSO INCORPORATES ROUTINES FOUND IN THE POTOMAC MICRO-MAGIC MODEM
;MANUAL WHICH MAY BE USED IF YOU HAVE A PMMI MODEM BOARD.

;		******* N O T I C E *********
;When using this modem program, you can avoid its shakey
;features (which show up primarily in batch mode) by always
;using the O or A option even if either modem is already in the
;originate or answer mode;  and, by always declaring the speed
;option such as .300, .600, etc.
		*****************************

;05/29/82  Moved fix log to history file.  MODEM7xx.HIS
					           RLP


	MACLIB MODEM76	;CONTAINS CMDLINE, INBUF, INLNCOMP, @7.61
		;DIR, AND MFACCESS ROUTINES
		;changed to MODEM.LIB by Jim Mills
		;to differentiate from other 'MACROS.LIB'


TRUE	EQU 0FFH
FALSE	EQU 0

CPM2X		EQU TRUE ;true if CP/M 2.X
			 ;false if CP/M 1.4 OR CDOS
DBUFSIZ		EQU 16	 ;BUFFER SIZE IN KBYTES

; PMMI EQUATES

PORT	EQU	0c0H	;PMMI BASE ADDRESS

MODCTLP	EQU	PORT	;MODEM CONTROL PORT
MODSNDB	EQU	1	;MODEM SEND BIT (XMIT BUFF EMPTY)
MODSNDR	EQU	1	;MODEM SEND READY
MODRCVB	EQU	2	;MODEM RECEIVE BIT (DAV)
MODRCVR	EQU	2	;MODEM RECEIVE READY
MODDATP	EQU	PORT+1	;MODEM DATA PORT
BAUDRP	EQU	PORT+2	;BAUD RATE PORT
MODCTL2	EQU	PORT+3	;2ND MODEM CONTROL PORT
ORIGMOD	EQU	1DH	;ORIGINATE MODE
ANSWMOD	EQU	1EH	;ANSWER MODE
BRKMSK	EQU	0FBH	;MASK TO SET BREAK
FRMER	EQU	20H	;FRAMING ERROR MASK
ORUNER	EQU	10H	;OVERRUN ERROR MASK
PARER	EQU	08H	;PARITY ERROR MASK
ODPARMSK EQU	0CFH	;MASK TO SET ODD PARITY
EVPARMSK EQU	20H	;MASK TO SET EVEN PARITY
NOPARMSK EQU	10H	;MASK TO RESET TO NO PARITY
ERRCDMSK EQU	38H	;MASK FOR ALL BITS EXCEPT ERROR CODES

WAITCTS	EQU	255	;number of seconds X 10 to wait for computer
			;tone after pmmi auto-dial function, 255 MAX.

CHGBAUD	EQU 'P'-40H	;USED IN TERMINAL MODE TO CHANGE
			;BAUD RATE 'ON THE FLY'
ERRLIM	EQU 10		;NUMBER OF TIMES TO RETRY
			;SEND/RECEIVE ERRORS BEFORE QUIT
BRKCHR	EQU '@'-40H	; ^@ = TRANSMIT "BREAK" WITH PMMI
EXITCHR	EQU 'E'-40H	; ^E = EXIT WITHOUT DISCONNECT
DISCCHR	EQU 'D'-40H	; ^D = DISCONNECT
TRANCHR	EQU 'T'-40H	; ^T = TRANSFER CHARACTER
CAN	EQU 'X'-40H	; ^X = CANCEL SEND/RECEIVE
EOFCHAR	EQU 'Z'-40H	; ^Z = END OF FILE
SAVECHR	EQU 'Y'-40H	; ^Y = SAVE CHARACTER
XOFF	EQU 'S'-40H	; ^S = XOFF CHARACTER
XON	EQU 'Q'-40H	; ^Q = XON CHARACTER
EXTCHR	EQU '^'-40H	; ^^ = SEND NXT CHR
SOH	EQU 1		; START OF HEADER
EOT	EQU 4		; END OF TEXT
ACK	EQU 6		; ACKNOWLEDGE
NAK	EQU 15H		; NOT ACKNOWLEDGE
CRC	EQU 'C'		;USED TO RQST CRC INSTEAD OF CLSUM
BDNMCH	EQU 75H		; BAD NAME MATCH
OKNMCH	EQU ACK		; OKAY NAME MATCH
LF	EQU 10		; LINEFEED
CR	EQU 13		; CARRIAGE RETURN
BELL	EQU 7		; BELL CHARACTER
FRONTPAN EQU 0FFH	; IMSAI FRONT PANEL

BOTTRAM	SET LAST+100H AND 0FF00H

	ORG 100H

	JMP	START

;THESE ROUTINES ARE AT THE BEGINNING OF THE PROGRAM SO
;THEY CAN BE PATCHED BY A MONITER WITHOUT RE-ASSEMBLING
;THE PROGRAM.

PMMIBYTE:	DB  TRUE	;true=pmmi modem
IMSAIBYTE:	DB  FALSE	;true=imsai front panel
FASTCLK:	DB  TRUE	;4 MHz or greater
BAKUPBYTE:	DB  FALSE	;true=make .BAK file
XPRFLG:		DB  TRUE	;true=no menu, false=print menu
PULSERATE:	DB  125		;125 FOR 20PPS, 250 FOR 10PPS dialing
SAVCCP:		DB  false	;true=do not overwrite CCP
CLDBOOT:	DW  0FC00H	;put your coldboot entry here. ;@7.63
IN$MODCTLP:	IN  MODCTLP ! RET ;in modem control port
OUT$MODDATP:	OUT MODDATP ! RET ;out modem data port
ANI$MODSNDB:	ANI MODSNDB ! RET ;bit to test for send ready
CPI$MODSNDR:	CPI MODSNDR ! RET ;value of send bit when ready
IN$MODDATP:	IN  MODDATP ! RET ;in modem data port
ANI$MODRCVB:	ANI MODRCVB ! RET ;bit to test for receive ready
CPI$MODRCVR:	CPI MODRCVR ! RET ;value of receive bit when ready
JMP$INITMOD:	JMP INITMOD	  ;to initialize port, if necessary
IN$BAUDRP:	IN  BAUDRP  ! RET ;in baudrate port
OUT$BAUDRP:	OUT BAUDRP  ! RET ;out baudrate port
OUT$MODCTL2:	OUT MODCTL2 ! RET ;out modem control port #2
OUT$MODCTLP:	OUT MODCTLP ! STA UARTCTLB ! RET ;out modem control port
						 ;and store control byte

CRFLAG:	DB 0	;CONTINUOUS REDIAL FLAG

; PHONE NUMBER LIBRARY TABLE FOR DIALING FROM LIBRARY
; OF NUMBERS STORED IN THESE DB'S AT ASSEMBLY-TIME.
; EACH DB MUST BE 30 CHARACTERS LONG FOR PROPER OPERATION.
; A 'DB 0' INDICATES NO DIALING, PROGRAM WILL DISCONNECT
; AND RETURN TO COMMAND MODE.  LAST DB MUST BE DB 0. UP TO
; 26 NUMBERS ARE ALLOWED.

NUMBLIB:
;	'----5---10---15---20---25---30'
DB	'A=Amrad           703-734-1387'	;'A'
DB	'B=Ben Bronson     312-955-4493'	;'B'
DB	'C=                            '	;'C'
DB	'D=C.Cliff C.C.    312-234-9257'	;'D'
DB	'E=Ron Fowler     313-729-1905R'	;'E'
DB	'F=                            '	;'F'
DB	'G=Gasnet NASA     301-344-9156'	;'G'
DB	'H=Dave Hardy      313-846-6127'	;'H'
DB	'I=Wayne Hammerly  301-953-3753'	;'I'
DB	'J=RBBS Pasadena   213-356-1034'	;'J'
DB	'K=David Kozinn    216-334-4604'	;'K'
DB	'L=Program Store   202-337-4694'	;'L'
DB	'M=Kelly Smith     805-527-9321'	;'M'
DB	'N=                            '	;'N'
DB	'O=SYSOP Sys       313-885-0506'	;'O'
DB	'P=CBBS Pasadena   213-799-1632'	;'P'
DB	'Q=R.Plouffe       703-524-2549'	;'Q'
DB	'R=Bruce Ratoff    201-272-1874'	;'R'
DB	'S=K.Peterson     313-759-6569R'	;'S'
DB	'T=Tech. CBBS      313-846-6127'	;'T'
DB	'U=PMMI            703-379-0303'	;'U'
DB	'V=                            '	;'V'
DB	'W=                            '	;'W'
DB	'X=                            '	;'X'
DB	'Y=                            '	;'Y'
DB	'Z=                            '	;'Z'
DB	0					; end

START:	LXI	H,0
	DAD	SP	;GET CP/M'S STACK
	SHLD	STACK	;SAVE IT
	LXI	SP,STACK ;START LOCAL STACK

	CALL	START1

	DB CR,LF,'MODEM 7.65 as of 05/29/82',cr,lf
	DB 'Originally Written by Ward Christensen',cr,lf,'$'


START1:	POP	D	;GET ADDRESS OF ABOVE MESSAGE
	MVI	C,PRINT	; 9
	CALL	BDOS

	CALL	INITADR	;INITIALIZE ADDRESSES
	MVI	A,TRUE	; 0FFH
	STA	NFILFLG
	CMA		; 0
	STA	SAVEFLG
	OUT	FRONTPAN ; IMSAI

	CALL	PROCOPT	;PROCESS CONTROL OPTIONS
	LDA	UARTFLG	; @7.61
	ORA	A	; @7.5 
	MVI	A,ANSWMOD ; @7.5
	STA	UARTCTLB  ; @7.5
	JNZ	START2	; @7.5
	MVI	A,ORIGMOD ; @7.5
	STA	UARTCTLB  ; @7.5
START2:	LDA	OPTION	;GET MAIN OPTION
	CPI	'X'	;EXPERT FLAG?
	JNZ	RESTART	;NO
	MVI	A,TRUE	;YES
	STA	XPRFLG	;MAKE EXPERT
	JMP	MENU

RESTART:
	LDA	OPTION	;GET MAIN OPTION
	MOV	B,A	;SAVE IT
	LDA	PMMIBYTE ;PMMI?
	ORA	A	;SET FLAGS
	MOV	A,B	;GET OPTION BACK
	JZ	S1	;NOT PMMI
	CPI	'C'	;CALL (DIAL) FUNCTION?
	JZ	DIALPL	;YES, GO TO IT

S1:	CPI	' '	;NO OPTION SPEC'D?
	JZ	MENU	;TRUE, GO MENU
	CPI	'M'	;MENU ASKED FOR?
	JZ	MENU2	;YES, GO MENU
	CALL	JMP$INITMOD
	CALL	MOVEFCB
	MVI	A,FALSE
	STA	NFILFLG

	CALL	IN$MODDATP ;GOBBLE UP GARBAGE..
	CALL	IN$MODDATP ;..CHARACTERS ON LINE

	LDA	OPTION	;PROCESS MAIN OPTION
	CPI	'E'	;ECHO MODE?
	JZ	TRMECHO	;YES
	CPI	'T'	;TERMINAL MODE?
	JZ	DSKSAVE	;YES
	CPI	'S'	;SEND A FILE?
	JZ	SENDFIL	;YES
	CPI	'R'	;RECEIVE A FILE?
	JZ	RCVFIL	;YES
	CPI	'D'	;DISCONNECT?
	JZ	DISCON1	;YES, DISCONNECT & GO MENU
	JMP	MENU	;NO OPTION SPEC'D, GO MENU

;REVISED TERMINAL ROUTINE ALLOWING MEMORY SAVE

DSKSAVE:
	LDA	NFILFLG	;NEW FILE FLAG
	CPI	TRUE	;OFFH? (TRUE=NORMAL TERMINAL MODE)
	JZ	TERM	;YES
	LDA	FCB+1	;FIRST CHAR OF FILENAME
	CPI	' '	;FILE SPEC'D
	JNZ	GOODNM	;YES, GOOD NAME
	MVI	A,TRUE	;0FFH
	STA	NFILFLG	;
	OUT	FRONTPAN ;0FFH PORT FOR IMSAI FRONT PANEL
	CMA		; 0
	STA	SAVEFLG	;
	JMP	TERM	;

GOODNM:	CALL	ERASFIL
	CALL	MOVE2
	LXI	D,FCB3
	MVI	C,MAKE
	CALL	BDOS
	LXI	D,FCB3
	MVI	C,OPEN
	CALL	BDOS
	LXI	H,BOTTRAM
	SHLD	HLSAVE
	MVI	A,FALSE
	STA	NFILFLG

TERM:
	LDA	UARTFLG	; @7.61
	sta	origsav	; @MODEM75.FIX
	ORA	A	; @7.5 
	MVI	A,ANSWMOD ; @7.5
	STA	UARTCTLB  ; @7.5
	JNZ	TERM2	; @7.5
	MVI	A,ORIGMOD ; @7.5
	STA	UARTCTLB  ; @7.5
TERM2:	CALL	STAT	;KEYPRESS?
	JZ	TERML	;NO, CHECK LINE
	CALL	KEYIN	;GET CHAR FROM KBD
	MOV	B,A	;SAVE
	LDA	EXACFL
	ORA	A	;EXACT?
	MVI	A,0
	STA	EXACFL	;CLR FOR NEXT TIME
	MOV	A,B	;RESTORE
	JNZ	NOTOG
	CPI	EXITCHR	;^E?
	JZ	MENU	;YES, RETURN TO MENU
	CPI	DISCCHR	;^D?
	JZ	DISCON1	;YES, DISCONNECT & RETURN TO MENU
	CPI	EXTCHR	;^^?
	JZ	EXTFLG	;YES, SET FLAG FOR NXT CHAR

	CPI	TRANCHR	;TEST FOR TRANSFER REQUEST (^T)
	CZ	TRANSFER ;SEND-A-FILE (BLIND SEND)
	JZ	TERM	;LOOP

	MOV	B,A
	LDA	PMMIBYTE
	ORA	A
	MOV	A,B
	JZ	S2
	CPI	BRKCHR	;BREAK?
	JZ	BREAK
	CPI	CHGBAUD
	PUSH	PSW
	PUSH	H
	CZ	NEWBAUD
	POP	H
	POP	PSW
	JZ	TERML

S2:	CPI	SAVECHR
	JNZ	NOTOG
	LDA	NFILFLG	;DO NOT ALLOW SAVE IF..
	CPI	TRUE	;..THIS FLAG IS SET.
	JZ	TERML
	LDA	SAVEFLG
	CMA
	STA	SAVEFLG
	JMP	TERML

EXTFLG:	MVI	A,TRUE
	STA	EXACFL
	JMP	TERML

NOTOG:	CALL	OUT$MODDATP

TERML:	CALL	IN$MODCTLP
	CALL	ANI$MODRCVB
	CALL	CPI$MODRCVR
	JNZ	TERM
	CALL	IN$MODDATP
	ANI	7FH	;STRIP PARITY
	JZ	TERM		
	CALL	TYPE
	PUSH	PSW
	LDA	SAVEFLG
	CPI	FALSE
	JZ	NOSAVE
	POP	PSW
	MOV	M,A
	INX	H
	SHLD	HLSAVE	;MENU COMMAND DESTROYS HL-REG..
		;..GET HL WHEN ENTERING VIA 'RET' CMD.
	MOV	B,A
	LDA	IMSAIBYTE
	ORA	A
	MOV	A,B
	JZ	COLON
	CMA		;FRONT PANEL SHOWS CHARS WHEN..
	OUT	FRONTPAN ;..MEMORY SAVE IS ACTIVE.
	JMP	NOCOLON
COLON:	CPI	LF	;IF NO FRONT PANEL, THEN..
	JNZ	NOCOLON	;..TYPE ":" AFTER EACH LINE FEED..
	MVI	A,':'	;..WHEN MEMORY SAVE ACTIVE.
	CALL	TYPE
NOCOLON:
	LDA	SAVCCP
	ORA	A
	JZ	SUB1
	LDA	7
	SBI	8	;..PAGE BELOW CCP ..
	JMP	SUB1A
SUB1:	LDA	7
SUB1A:	DCR	A	;..OR BDOS HAS BEEN..
	CMP	H	;..REACHED AND DISKSAVE IS NEEDED.
	CZ	INTDSKSV

	JMP	TERM
NOSAVE:	POP	PSW
	JMP	TERM

SAVEFLG:	DB FALSE
LASTBYT1:	DB 0
LASTBYT2:	DB 0

INTDSKSV:
	MVI	A,XOFF	;SEND A CTRL-S TO STOP..
	CALL	OUT$MODDATP ;..REMOTE COMPUTER OUTPUT.

	MVI	D,0	;D IS THE BUFFER COUNT
	CALL	INMODEM	;GET LAST BYTES SENT..
	STA	LASTBYT1 ;..AFTER CTRL-S.
	CALL	INMODEM	;ADD MORE CALLS TO INMODEM..
	STA	LASTBYT2 ;..AND STA LASTBYT# IF YOU ARE..
			;..LOSING BYTES WHEN MEMORY IS FULL.
	PUSH	D
	CALL	NUMREC1
	CALL	WRTDSK	;WRITE THE RECORDS
	POP	D

	LXI	H,BOTTRAM
	INR	D
	DCR	D	;TEST BUFFER COUNT FOR ZERO
	JZ	CTRLQ
	LDA	LASTBYT1 ;GET THE LAST BYTES THAT WERE..
	MOV	M,A	;..SAVED AND PUT THEM IN..
	INX	H	;..BOTTRAM.
	CALL	TYPE
	DCR	D
	JZ	CTRLQ
	LDA	LASTBYT2
	MOV	M,A
	INX	H
	CALL	TYPE

CTRLQ:	MVI	A,XON	;SEND START CHARACTER..
	CALL	OUT$MODDATP ;..TO REMOTE COMPUTER.

	RET

BREAK:	PUSH	D	;SAVE IT
	LXI	D,0	;ZERO IT
	LDA	MODCTLB	;GET THE LAST MODEM CONTROL BYTE
	ANI	0FBH	;SET THE TRANSMIT BREAK BIT LOW - ACTIVE LOW
	CALL	OUT$MODCTL2 ;SEND IT TO THE MODEM
	LDA	FASTCLK	;GET FAST CLOCK FLAG
	ORA	A	;SET FLAGS
	LXI	B,450	;BREAK DELAY COUNTER FOR SLOW CLOCK
	JZ	BRK1	;JUMP IF NOT FAST CLOCK
	LXI	B,900	;BREAK DELAY COUNTER FOR FAST CLOCK
BRK1:	CALL	TIMERL
	JZ	BRK2	;IF TIME IS UP RESET BREAK
	CPI	0	;CHECK FOR NULLS
	JZ	BRK1	;DON'T PROCESS THEM
	ANI	7FH	;STRIP PARITY
	CALL	TYPE
	PUSH	PSW
	LDA	SAVEFLG
	CPI	FALSE
	JZ	NOSAVEB
	POP	PSW
	MOV	M,A
	INX	H
	SHLD	HLSAVE	;MENU COMMAND DESTROYS HL-REG..
		;..GET	HL WHEN ENTERING VIA 'RET' CMD.
	PUSH	D
	MOV	D,A
	LDA	IMSAIBYTE
	ORA	A
	MOV	A,D
	POP	D
	JZ	COLONB
	CMA	;FRONT	PANEL SHOWS CHARS WHEN..
	OUT	FRONTPAN ;..MEMORY SAVE IS ACTIVE.
	JMP	BRK1
COLONB:	CPI	LF	;IF NO FRONT PANEL, THEN..
	JNZ	BRK1	;..TYPE ":" AFTER EACH LINE FEED..
	MVI	A,':'	;..WHEN MEMORY SAVE ACTIVE.
	CALL	TYPE
	JMP	BRK1	;@
NOSAVEB: POP	PSW	;RESTORE IT
	JMP	BRK1

BRK2:	LDA	MODCTLB	;GET MODEM CONTROL BYTE
	CALL	OUT$MODCTL2
	POP	D
	LHLD	HLSAVE	;LAST ADDRESS WRITTEN IF DATA BEING SAVED
	LDA	SAVCCP
	ORA	A
	JZ	SUB2
	LDA	7	;CHECK TO SEE IF..
	SBI	8	;..PAGE BELOW CCP ..
	JMP	SUB2A
SUB2:	LDA	7
SUB2A:	DCR	A	;..OR BDOS HAS BEEN ..
	CMP	H	;..REACHED AND DISKSAVE IS NEEDED.
	JNZ	TERM	;NO PROBLEM - GO BACK TO NORMAL ROUTINE
	CALL	ILPRT
DB	CR,LF,'Memory save buffer full',CR,LF,BELL,0
	JMP	TERM


;THIS SUBROUTINE WILL LOOP UNTIL THE MODEM RECEIVES A CHARACTER
;OR 100 MILLISECONDS. IF A CHARACTER IS RECEIVED, A FLAG IS SET
;TO STORE THE CHARACTER. A MAXIMUM OF TWO CHARACTERS ARE STORED,
;BUT MORE MAY BE STORED IF DESIRED (SEE COMMENT IN "INTDSKSV"
;ABOVE).

INMODEM:
	LDA	FASTCLK
	ORA	A
	LXI	B,1250
	JZ	TIMERL
	LXI	B,2500
TIMERL:	CALL	IN$MODCTLP
	CALL	ANI$MODRCVB
	CALL	CPI$MODRCVR
	JZ	GETBYTE
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	TIMERL
	RET
GETBYTE:
	CALL	IN$MODDATP
	INR	D
	RET

NUMRECS:
	MVI	M,EOFCHAR
	INX	H
	LXI	D,127
	DAD	D
NUMREC1:
	LXI	D,-(BOTTRAM)
	DAD	D

	MOV	A,L	;DIVIDE HL BY 128..
	ORA	A
	RAL		;..TO GET THE..
	MOV	L,H	;..NUMBER OF SECTORS
	MVI	H,0
	PUSH	PSW
	DAD	H
	POP	PSW
	MVI	A,0
	ADC	L
	MOV	L,A	;RETURNS WITH NUMBER OF..
	RET		;..128 BYTE RECORDS IN HL.

WRTDSK:	LXI	D,BOTTRAM
NEXTWRT:
	MVI	C,STDMA
	CALL	BDOSRT
	PUSH	D
	LXI	D,FCB3
	MVI	C,WRITE
	CALL	BDOSRT
	POP	D
	XCHG
	PUSH	D
	LXI	D,128
	DAD	D
	POP	D
	XCHG
	DCX	H
	MOV	A,H
	ORA	L
	JNZ	NEXTWRT
	RET

CLOSE3:	LXI	D,FCB3
	MVI	C,CLOSE
	CALL	BDOS
	RET

BDOSRT:	PUSH B ! PUSH D ! PUSH H ! PUSH PSW
	CALL	BDOS
	POP PSW ! POP H ! POP D ! POP B
	RET

MOVE2:	LXI	H,FCB3
	CALL	INITFCBS
	LXI	H,FCB
	LXI	D,FCB3
	MVI	B,12
	CALL	MOVE
	RET

;FILE TRANSFER ROUTINE - CALLED WITH 
;CONTROL-T FROM TERMINAL ROUTINE.
;TRANSFER MAY BE CANCELLED WHILE SENDING BY USING CONTROL-X.

TRANSFER:
	PUSH H ! PUSH D ! PUSH B ! PUSH PSW
	LXI	H,FCB4
	CALL	INITFCBS ;INITIALIZES FCBS POINTED..
	LXI	H,FCB+16 ;..TO BY HL REG.
	CALL	INITFCBS
GET:	CALL	GETNAME
	LDA	CMDBUF+2 ;WAS FILE ENTERED
	CPI	20H
	JZ	TRANSL2
	CALL	MOVE4
	CALL	OPEN4
	CPI	0FFH	;RETURN WITH 0FFH MEANS
	JNZ	CONTIN	;FILE DOES NOT EXIST
TRANSL1: CALL	ILPRT
DB	CR,LF,'++File does not exist++',CR,LF,0
TRANSL2: CALL ILPRT
DB	'Type "R" to return to modem',CR,LF
DB	'Type "A" to re-enter name: ',BELL,0
	CALL	KEYIN
	CALL	UCASE
	CALL	TYPE	;ECHO RESPONSE
	CALL	CRLF
	CPI	'A'
	JZ	GET
	CPI	'R'
	JZ	RETURN
	JMP	TRANSL2

CONTIN:	LXI	D,80H
	MVI	C,STDMA
	CALL	BDOS
READMR:	CALL	READ80
	CPI	1	;END OF FILE
	JZ	RETURNS
	CPI	2	;BAD READ
	JZ	RETURNU
	CALL	SEND80C
	CPI	EOFCHAR	;END OF FILE - OMIT IF OBJECT..
	JZ	RETURNS	;..CODE IS TO BE SENT.
	CPI	CAN	;CANCELLATION?
	JZ	TRANCAN
	JMP	READMR
RETURNS:
	CALL	ILPRT
DB	CR,LF,'++File transfer completed++',CR,LF,BELL,0
	JMP	RETURN
RETURNU:
	CALL	ILPRT
DB	CR,LF,'++File transfer unsuccessful++',CR,LF,BELL,0
	JMP	RETURN
TRANCAN:
	CALL	ILPRT
DB	CR,LF,CR,LF,'++ Transfer cancelled ++',CR,LF,BELL,0
RETURN:	POP PSW ! POP B ! POP D ! POP H
	RET

INITFCBS:		;ENTRY AT +2 WILL LEAVE..
	MVI	M,0	;..DRIVE NO. INTACT.
	INX	H	;WILL INITIALIZE AN FCB..
	MVI	B,11	;..POINTED TO BY HL-REG. FILLS 1ST POS
LOOP10:	MVI	M,' '	;..WITH 0, NEXT 11 WITH..
	INX	H	;..WITH BLANKS, AND LAST..
	DCR	B	;..21 WITH NULLS.
	JNZ	LOOP10
	MVI	B,21
LOOP11:	MVI	M,0
	INX	H
	DCR	B
	JNZ	LOOP11
	RET

GETNAME: CALL ILPRT
DB	CR,LF,'Enter file name to be transferred -  C/R TO QUIT: ',0
	LXI	D,CMDBUF
	CALL	INBUFF
	CALL	CRLF
	RET

MOVE4:	LXI	D,CMDBUF
	LXI	H,FCB4
	CALL	CPMLINE
	RET

OPEN4:	LXI	D,FCB4
	MVI	C,OPEN
	CALL	BDOS
	RET

READ80:	LXI	D,FCB4
	MVI	C,READ
	CALL	BDOS
	RET

SEND80C:
	MVI	B,80H
	LXI	H,80H
SENDCH1:
	MOV	A,M
	CALL	MODOUT
	CPI	EOFCHAR
	RZ
	CALL	STAT	;TEST TO SEE IF
	ORA	A	;CANCELLATION REQUESTED
	JZ	SKIP12
	CALL	KEYIN
	CPI	CAN
	RZ
SKIP12:	INX	H
	DCR	B
	JNZ	SENDCH1
	RET

MODOUT:	PUSH	PSW
MODOUTL:
	CALL	IN$MODCTLP
	CALL	ANI$MODSNDB
	CALL	CPI$MODSNDR
	JNZ	MODOUTL
	POP	PSW
	CALL	OUT$MODDATP
	CALL	TYPE
	RET

FCB4:	DS	33

;TERMINAL ECHO MODE

TRMECHO:
	CALL	IN$MODCTLP
	CALL	ANI$MODRCVB
	CALL	CPI$MODRCVR
	JZ	LINECHR
	CALL	STAT
	JZ	TRMECHO
	CALL	KEYIN
	CPI	EXITCHR
	JZ	MENU

	MOV	B,A
	LDA	PMMIBYTE
	ORA	A
	MOV	A,B
	JZ	S3
	CPI	CHGBAUD	;SAME ROUTINE AS IN TERMINAL MODE
	PUSH	PSW
	CZ	NEWBAUD
	POP	PSW
	CPI	CHGBAUD

	JZ	TRMECHO
S3:	CALL	OUT$MODDATP
	CALL	TYPE
	JMP	TRMECHO

LINECHR:
	CALL	IN$MODDATP
	CALL	OUT$MODDATP
	CALL	TYPE
	JMP	TRMECHO


;		SEND A CP/M FILE

SENDFIL:
	CALL	PARITY	;SET PARITY IF REQUESTED
	LDA	BATCHFLG ;CHECK IF MULTIPLE FILE..
	ORA	A	;..MODE IS SET.
	JNZ	SENDC1
	MVI	A,TRUE	;INDICATE BATCH SEND
	STA	SENDFLG
	LDA	FSTFLG	;IF FIRST TIME THRU..
	ORA	A	;..SCAN THE COMMAND LINE..
	CNZ	TNMBUF	;..FOR MULTIPLE NAMES.
	CALL	SENDFN	;SENDS FILE NAME TO RECEIVER
	JNC	SENDC2	;CARRY SET MEANS NO MORE FILES.
	MVI	A,'B'	;STOP BATCH..
	STA	BATCHFLG ;..MODE OPTION.
	MVI	A,EOT	;FINAL XFER END
	CALL	SEND
	JMP	DONE
SENDC1:	LDA	FCB+1
	CPI	' '
	JZ	BLKFILE
SENDC2:	CALL	CNREC	;GET NUMBER OF RECORDS
	CALL	OPENFIL
	MVI	E,80
	CALL	WAITNAK
SENDLP:	CALL	RDSECT
	JC	SENDEOF
	CALL	INCRSNO
	XRA	A
	STA	ERRCT
SENDRPT:
	CALL	SENDHDR
	CALL	SENDSEC
	LDA	CRCFLG
	ORA	A
	CZ	SENDCRC
	CNZ	SENDCKS
	CALL	GETACK
	JC	SENDRPT
	JMP	SENDLP

SENDEOF:
	MVI	A,EOT
	CALL	SEND
	CALL	GETACK
	JC	SENDEOF
	JMP	DONE

;		RECEIVE A FILE

RCVFIL:	xra	a	;@7.41 - default to CRC mode
	sta	CRCFLG	;@7.41
RCVFIL1:		;@FIX7.61
	CALL	PARITY	;SET PARITY IF REQUESTED
	LDA	BATCHFLG ;CHECK IF MULT..
	ORA	A	;..FILE MODE.
	JNZ	RCVC1
	MVI	A,FALSE	;FLAG WHERE TO RETURN..
	STA	SENDFLG	;..FOR NEXT FILE TRANS.
	CALL	GETFN	;GET THE FILE NAME.
	JNC	RCVC2	;CARRY SET MEANS NO MORE FILES.
	MVI	A,'B'	;STOP BATCH..
	STA	BATCHFLG ;..MODE OPTION.
	JMP	DONE
RCVC1:	LDA	FCB+1	;MAKE SURE FILE IS NAMED
	CPI	' '
	JZ	BLKFILE
	JMP	RCVC3
RCVC2:	CALL	CKCPM2
	CALL	CKBAKUP
RCVC3:	CALL	ERASFIL
	CALL	MAKEFIL
	lda	batchflg;@75.FIX.................
	ora	a	;DON'T PRINT MSSG IF..
	jnz	rcvc4	;..IN MULTI AND QUIET.
	lda	qflg
	ora	a
	jz	rcvfst
rcvc4:	CALL	ILPRT	;@75.FIX.................
DB	'File open, ready to receive',CR,LF,0
RCVFST:	LDA	CRCFLG
	ORA	A
	MVI	A,NAK
	JNZ	RCVFIL2
	MVI	A,CRC
;
RCVFIL2:
	CALL	SEND
	lda	CRCFLG		;@7.41...............................
	ora	a
	jnz	RCVNAKM		;if in CRC mode
	call	ILPRT		;then say so
	DB	'CRC in effect',cr,lf,0
	jmp	RCVLP
RCVNAKM:
	call	ILPRT		;else say checksum mode
	DB	'Checksum in effect',cr,lf,0
				;@7.41...............................
RCVLP:	CALL	RCVSECT
	JC	RCVEOT
	CALL	WRSECT
	CALL	INCRSNO
	CALL	SENDACK
	JMP	RCVLP

RCVEOT:	CALL	WRBLOCK
	CALL	SENDACK
	CALL	CLOSFIL
	JMP	DONE
	
;SUBROUTINES

SENDFN:	LDA	QFLG
	ORA	A
	JZ	SWNAK
	CALL	ILPRT
DB	'Awaiting name NAK',CR,LF,0
SWNAK:	MVI	E,80
	CALL	WAITNLP
	MVI	A,ACK	;GOT NAK, SEND ACK
	CALL	SEND
	LXI	H,FILECT
	DCR	M
	JM	NOMRNM
	LHLD	NBSAVE	;GET FILE NAME..
	LXI	D,FCB	;..IN FCB
	MVI	B,12
	CALL	MOVE
	SHLD	NBSAVE
	CALL	SENDNM	;SEND IT
	ORA	A	;CLEAR CARRY
	RET
NOMRNM:	MVI A,EOT
	CALL SEND
	STC
	RET

SENDNM:	PUSH	H
SENDNM1:
	MVI	D,11	;COUNT CHARS IN NAME
	MVI	C,0	;INIT CHECKSUM
	LXI	H,FCB+1	;ADDRESS NAME
NAMLPS:	MOV	A,M	;SEND NAME
	ANI	7FH	;STRIP HIGH ORDER BIT SO CP/M 2..
	CALL	SEND	;..WON'T SEND R/O FILE DESIGNATION.
	LDA	QFLG	;SHOW NAME IF..
	ORA	A	;..QFLG NOT SET.
	MOV	A,M
	CNZ	TYPE
ACKLP:	PUSH	B	;SAVE CKSUM
	MVI	B,1	;WAIT FOR RECEIVER..
	CALL	RECV	;..TO ACKNOWLEDGE..
	POP	B	;..GETTING LETTER.
	JC	SCKSER
	CPI	ACK
	JNZ	ACKLP
	INX	H	;NEXT CHAR
	DCR	D
	JNZ	NAMLPS
	MVI	A,EOFCHAR ;TELL RECEIVER END OF NAME
	CALL	SEND
	LDA	QFLG
	ORA	A
	CNZ	CRLF
	MOV	D,C	;SAVE CHECKSUM
	MVI	B,1
	CALL	RECV	;GET CHECKSUM..
	CMP	D	;..FROM RECEIVER.
	JZ	NAMEOK
SCKSER:	MVI	A,BDNMCH ;BAD NAME-TELL RECEIVER
	CALL	SEND
	LDA	QFLG
	ORA	A
	JZ	SKCSER1
	CALL	ILPRT
DB	'Checksum error',CR,LF,0
SKCSER1:
	MVI	E,80	;DO HANDSHAKING OVER
	CALL	WAITNLP	;DON'T PRINT "AWAITING NAK" MSG
	MVI	A,ACK
	CALL	SEND
	JMP	SENDNM1
NAMEOK:	MVI	A,OKNMCH ;GOOD NAME-TELL RECEIVER
	CALL	SEND
	POP	H
	RET	

GETFN:	LXI	H,FCB
	CALL	INITFCBS+2 ;DOES NOT INITIALIZE DRIVE
	LDA	QFLG
	ORA	A
	JZ	GNAMELP
	CALL	ILPRT
DB	'Awaiting file name',CR,LF,0
GNAMELP:
	CALL	HSNAK
	JC	GNAMELP
	CALL	GETNM	;GET THE NAME
	CPI	EOT	;IF EOT, THEN NO MORE FILES
	JZ	NOMRNMG
	ORA	A	;CLEAR CARRY
	RET
NOMRNMG:
	STC
	RET

GETNM:	PUSH	H
GETNM1:	MVI	C,0	;INIT CHECKSUM
	LXI	H,FCB+1
NAMELPG:
	MVI	B,5
	CALL	RECV	;GET CHAR
	JNC	GETNM3
	LDA	QFLG
	ORA	A
	JZ	GETNM2
	CALL	ILPRT
DB	'Time out receiving filename',CR,LF,0
GETNM2:	JMP	GCKSER
GETNM3:	CPI	EOT	;IF EOT, THEN NO MORE FILES
	JZ	GNRET
	CPI	EOFCHAR	;GOT END OF NAME
	JZ	ENDNAME
	MOV	M,A	;PUT NAME IN FCB
	LDA	QFLG	;TYPE IT IF NO QFLG
	ORA	A
	MOV	A,M
	CNZ	TYPE
	PUSH	B	;SAVE CKSUM
	MVI	A,ACK	;ACK GETTING LETTER
	CALL	SEND
	POP	B
	INX	H	;GET NEXT CHAR
	MOV	A,L	;DON'T LET NOISE...
	CPI	7FH	;..CAUSE OVERFLOW..
	JZ	GCKSER	;..INTO PROGRAM AREA.
	JMP	NAMELPG
ENDNAME:
	LDA	QFLG
	ORA	A
	CNZ	CRLF
	MOV	A,C	;SEND CHECKSUM
	CALL	SEND
	MVI	B,1
	CALL	RECV	;CHECKSUM GOOD?
	CPI	OKNMCH	;YES IF OKNMCH SENT..
	JZ	GNRET	;..ELSE DO OVER.
GCKSER:	LXI	H,FCB	;CLEAR FCB (EXCEPT DRIVE)..
	CALL	INITFCBS+2 ;..SINCE IT MIGHT BE DAMAGED..
	LDA	QFLG	;..BY TOO MANY CHARS.
	ORA	A
	JZ	GCKSER1
	CALL	ILPRT
DB	'Checksum error',CR,LF,0
GCKSER1:
	CALL	HSNAK	;DO HANDSHAKING OVER
	JC	GCKSER1
	JMP	GETNM1
GNRET:	POP	H
	RET

HSNAK:	MVI	A,NAK	;SEND NAK UNTIL..
	CALL	SEND	;..RECEIVING ACK.
	CALL	CKABORT	;DON'T GET HUNG UP HERE
	MVI	B,2	;WAIT 2 SECONDS..
	CALL	RECV	;..IN RECEIVE.
	CPI	ACK	;IF ACK,RETURN WITH..
	RZ		;..CARRY CLEAR.
	STC
	RET

TNMBUF:	MVI	A,FALSE	;CALL FROM SENDFIL ONLY ONCE.
	STA	FSTFLG
	STA	FILECT
	CALL	SCAN
	LXI	H,NAMEBUF
	SHLD	NBSAVE	;SAVE ADDR OF 1ST NAME
TNLP1:	CALL	TRTOBUF
	LXI	H,FCB
	LXI	D,FCBBUF
	CALL	CPMLINE	;PARSE NAME TO CP/M FORMAT
TNLP2:	CALL	MFNAME	;SEARCH FOR NAMES (* FORMAT)
	JC	NEXTNM
	LDA	FCB+10	;IF CP/M 2 $SYS FILE..
	ANI	80H	;..DON'T SEND
	JNZ	TNLP2
	LHLD	NBSAVE	;GET NAME
	LXI	D,FCB	;MOVE IT TO FCB
	XCHG
	MVI	B,12
	CALL	MOVE
	XCHG
	SHLD	NBSAVE	;ADDR OF NEXT NAME
	LXI	H,FILECT	;COUNT FILES FOUND
	INR	M
	JMP	TNLP2
;
NEXTNM:	LXI	H,NAMECT ;COUNT NAMES FOUND
	DCR	M
	JNZ	TNLP1
	LXI	H,NAMEBUF ;SAVE START OF BUFFER
	SHLD	NBSAVE
	LDA	FILECT
	CPI	65	;NO MORE THAN 64 TRANSFERS
	RC
	MVI	A,64	;ONLY X'FER FIRST 64
	STA	FILECT
	RET

;SCANS CMDBUF COUNTING NAMES AND PUTTING DELIMITER (SPACE)
;AFTER LAST NAME

SCAN:	PUSH	H
	LXI	H,NAMECT
	MVI	M,0
	LXI	H,CMDBUF+1 ;FIND END OF CMD LINE..
	MOV	C,M	;..AND PUT SPACE THERE.
	MVI	B,0
	LXI	H,CMDBUF+2
	DAD	B
	MVI	M,20H
	LXI	H,CMDBUF+1
	MOV	B,M
	INR	B
	INR	B
SCANLP1:
	INX	H
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	20H
	JNZ	SCANLP1
SCANLP2:
	INX	H	;EAT EXTRA SPACES
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	20H
	JZ	SCANLP2
	SHLD	BGNMS	;SAVE START OF NAMES IN CMDBUF
	INR	B
	DCX	H
SCANLP3:
	INX	H
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	20H
	JNZ	SCANLP3
	LDA	NAMECT	;COUNTS NAMES
	INR	A
	STA	NAMECT
SCANLP4:
	INX	H	;EAT SPACES
	DCR	B
	JZ	DNSCAN
	MOV	A,M
	CPI	20H
	JZ	SCANLP4
	JMP	SCANLP3
;
DNSCAN:	MVI	M,20H	;SPACE AFTER LAST CHAR
	POP	H
	RET

;PLACES NEXT NAME IN BUFFER SO CPMLINE MAY PARSE IT
TRTOBUF:
	LHLD	BGNMS
	MVI	B,0
	LXI	D,FCBBUF+2
TBLP:	MOV	A,M
	CPI	20H
	JZ	TRBFEND
	STAX	D
	INX	H
	INX	D
	INR	B	;COUNT CHARS IN NAME
	JMP	TBLP
;
TRBFEND:
	INX	H
	MOV	A,M	;EAT EXTRA SPACES
	CPI	20H
	JZ	TRBFEND
	SHLD	BGNMS
	LXI	H,FCBBUF+1 ;PUT # CHARS BEFORE NAME
	MOV	M,B
	RET

;IN CP/M V.2, IF FILE IS R/O OR SYS, IT IS CHANGED TO 'BAK'.
CKCPM2:	MVI	C,12
	CALL	BDOS
	ORA	A	;RETURN 0 MEANS CP/M 1
	RZ
	MVI	C,STDMA
	LXI	D,80H
	CALL	BDOS
	MVI	C,SRCHF	;SEARCH FOR FILE
	LXI	D,FCB
	CALL	BDOS
	CPI	0FFH
	RZ
	ADD A ! ADD A	;MULT A-REG BY..
	ADD A ! ADD A	;..32 TO FIND..
	ADD	A	;..NAME IN DMA.
	LXI	H,80H
	ADD	L
	MOV	L,A	;HL POINTS TO DIR NAME
	LXI	D,9
	DAD	D	;POINT TO R/O ATTRIB BYTE
	MOV	A,M
	ANI	80H	;TEST MSB
	JNZ	MKCHG	;IF SET, MAKE CHANGE
	INX	H	;CHECK SYSTEM ATTRIB BYTE
	MOV	A,M
	ANI	80H
	RZ		;NOT $SYS OR $R/O
	DCX	H
MKCHG:	LXI	D,-8
	DAD	D	;POINT HL TO FILENAME + 1
	LXI	D,FCB+1	;MOVE DIR NAME TO FCB..
	MVI	B,11	;..WITHOUT CHANGING DRIVE.
	CALL	MOVE
	LXI	H,FCB+9	;R/O ATTRIB
	MOV	A,M
	ANI	7FH	;STRIP R/O ATTRIB
	MOV	M,A
	INX	H	;SYS ATTRIB
	MOV	A,M
	ANI	7FH
	MOV	M,A
	LXI	D,FCB
	MVI	C,30	;SET NEW ATTRIBS IN DIR
	CALL	BDOS

;MAY BE CALLED BY CKBAKUP BELOW. ITS RETURN DONE HERE
PLANCHG:
	LXI	H,FCB	;CHANGE NAME TO TYPE "BAK"
	LXI	D,6CH
	MVI	B,9	;MOVE DRIVE AND NAME (NOT TYPE)
	CALL	MOVE
	LXI	H,75H	;START OF TYPE IN FCB2
	MVI	M,'B'
	INX	H
	MVI	M,'A'
	INX	H
	MVI	M,'K'
	LXI	D,6CH
	MVI	C,ERASE	;ERASE ANY PREV BACKUPS
	CALL	BDOS
	LXI	H,6CH	;FCB2 DR FIELD SHOULD..
	MVI	M,0	;..0 FOR RENAME.
	LXI	D,FCB
	MVI	C,23	;RENAME
	CALL	BDOS
	RET

CKBAKUP:
	LDA	BAKUPBYTE
	ORA	A
	RZ
	MVI	C,SRCHF
	LXI	D,FCB
	CALL	BDOS
	INR	A
	RZ	;FILE NOT FOUND
	JMP	PLANCHG	;IN "CKCPM2" - RET DONE THERE

;MULTI-FILE ACCESS SUBROUTINE FROM CP/M USER'S GROUP
;FIXED BY MARK ZEIGER 8/17/80
;CARRY IS SET IF NO MORE NAMES CAN BE FOUND

MFNAME:	MFACCESS	;A MACRO IN MACROS.LIB

RCVSECT:
	XRA	A
	STA	ERRCT
RCVRPT:	XRA	A	;ZERO ACCUM
	STA	ERRCDE	;CLEAR RECEIVE ERROR CODE
	LDA	QFLG
	ORA	A
	JZ	RCVSQ
	CALL	ILPRT
DB	CR,'Awaiting # ',0
	PUSH	H	;SAVE IT
	LHLD	SECTNO	;GET SECTOR NUMBER
	INX	H	;BUMP IT
	CALL	DECOUT	;PRINT SECTOR NUMBER IN DECIMAL
	CALL	ILPRT
	DB	' (', 0
	CALL	DHXOUT	;16 BIT HEX CONVERSION & OUTPUT
	CALL	ILPRT
	DB	'H)',0
	MOV	A,L	;ONLY LOW BYTE USED BY PROGRAM
	POP	H	;RESTORE IT
;
;---->	RCVSQ:
;	If CRC is in effect, there is only a 3 second wait
;	for the first SOH.  If the SOH is not received within
;	3 seconds, then a NAK is sent which tells the sender
;	to use checksum checking instead of CRC.  This allows
;	automatic compatability with versions of XMODEM that
;	do not implement Cyclic Redundancy Checking(CRC).
;
RCVSQ:	LDA	FIRSTME	;first SOH...
	ORA	A	;...been received?
	JZ	RCVSQ2	;yes, go get next SOH
	XRA	A	;turn off...
	STA	FIRSTME	;...first soh recvd switch
	LDA	CRCFLG	;CRC in...
	ORA	A	;...effect?
	JNZ	RCVSQ2	;no, do long wait for first SOH
	MVI	B,7	;wait for upto 7 seconds	<---- JKM MOD 7/16/82
	CALL	RECV	;get a character from modem
	JNC	RCVSQ3	;got a char, go see if SOH
	CALL	ILPRT
DB	CR,LF,'++Switching to CHECKSUM MODE++',CR,LF,0
	MVI	A,'C'	;turn off...
	STA	CRCFLG	;...CRC mode.
	MVI	A,NAK	;send NAK to tell sender checksum
	CALL	SEND	;...is in effect & to start sending.
	JMP	RCVSECT	;go start receiving sector
;
RCVSQ2:	MVI	B,7	;10 IN ORIG PROG
	CALL	RECV
	JC	RCVSTOT
;
RCVSQ3:	CALL	RCVERR	;CHECK FOR ERRORS
	JC	RCVDERR	;JUMP IF THERE WAS AN ERROR
	CPI	SOH
	JZ	RCVSOH
	ORA	A
	JZ	RCVSQ
	CPI	EOT
	STC
	RZ
	MOV	B,A
	LDA	VSEEFLG
	ORA	A
	JZ	RCVSEH
	LDA	QFLG
	ORA	A
	JZ	RCVSERR

RCVSEH:	MOV	A,B
	CALL	CRLF
	CALL	HEXO
	CALL	ILPRT
DB	'H recv''d, not SOH',CR,LF,0

RCVSERR: MVI	B,1
	CALL	RECV
	JNC	RCVSERR
	MVI	A,NAK
	CALL	SEND
	LDA	ERRCT
	INR	A
	STA	ERRCT
	CPI	ERRLIM
	JC	RCVRPT
	LDA	VSEEFLG
	ORA	A
	JZ	RCVCKQ
	LDA	QFLG
	ORA	A
	JZ	RCVSABT
RCVCKQ:	CALL	CKQUIT
	JZ	RCVSECT
RCVSABT:
	CALL	CLOSFIL
	CALL	ERXIT
DB	CR,LF,'++Unable to receive block - Aborting++',CR,LF,'$'

RCVSTOT:
	LDA	VSEEFLG
	ORA	A
	JZ	RCVSPT
	LDA	QFLG
	ORA	A
	JZ	RCVSERR
RCVSPT:	CALL	ILPRT
DB	CR,LF,'++  Timeout ++ ',0
RCVPRN:	LDA	ERRCT
	CALL	HEXO
	CALL	CRLF
	JMP	RCVSERR

;---->	RCVERR:
;	Checks for framing, overrun, and parity errors. Parity errors
;	cannot be detected unless the parity option has been selected.
;	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, routine returns with carry flag set.

RCVERR:	PUSH	PSW	;SAVE CHAR TRANSMITTED
	LDA	ERRCDE	;GET RECEIVE ERROR CODE
	ANA	A	;IS IT ZERO?
	JZ	RCVERR2	;YES, NO RECEIVE ERROR
	POP	PSW	;RESTORE CHAR TRANSMITTED
	STC		;SET CARRY ON TO INDICATE AN ERROR
	RET

RCVERR2:
	POP	PSW	;RESTORE CHAR TRANSMITTED
	RET

;---->	RCVDERR: Checks for a receive error and displays appropriate error
;	message. Then goes to RCVSERR to purge the line and send a NAK.

RCVDERR:
	LDA	VSEEFLG	;VIEWING
	ORA	A	;...MODE?
	JZ	RCVDERRP ;YES,..PRT MSG
	LDA	QFLG	;QUIET...
	ORA	A	;...MODE?
	JZ	RCVSERR	;YES, NO MSG

RCVDERRP: CALL ILPRT
	DB	CR,LF,0
	LDA	ERRCDE	;GET RECEIVE ERR CODE
	ANI	FRMER	;WAS THERE A FRAMING ERROR?
	JZ	RCVDERR2 ;NO, GO CHECK FOR OVERRUN
	CALL	ILPRT
DB	'++Framing error++ ',0
	CALL	RCVDERR5 ;PRINT # OF ERROR

RCVDERR2:
	LDA	ERRCDE	;GET RECEIVE ERR CODE
	ANI	ORUNER	;WAS THERE AN OVERRUN
	JZ	RCVDERR3 ;NO, GO CHECK FOR PARITY ERROR
	CALL	ILPRT
DB	'++Overrun error++ ',0
	CALL	RCVDERR5

RCVDERR3:
	LDA	ERRCDE	;GET RECEIVE ERR CODE
	ANI	PARER	;WAS THERE A PARITY ERROR?
	JZ	RCVDERR4 ;NO, GO PURGE LINE
	CALL	ILPRT
DB	'++Parity error++ ',0
	CALL	RCVDERR5

RCVDERR4: JMP	RCVSERR	;GO PURGE LINE, SEND NAK

;Display the number of the error, do a carriage return and line feed.
RCVDERR5:
	LDA	ERRCT	;GET ERROR NUMBER
	CALL	HEXO	;DISPLAY IT
	CALL	CRLF	;DO CR, LF
	RET

RCVSOH:	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	CALL	RCVERR	;CHECK FOR RECEIVE ERROR
	JC	RCVDERR
	MOV	D,A
	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	CALL	RCVERR	;CHECK FOR RECEIVE ERROR
	JC	RCVDERR
	CMA
	CMP	D
	JZ	RCVDATA
	LDA	VSEEFLG
	ORA	A
	JZ	RCVBSE
	LDA	QFLG
	ORA	A
	JZ	RCVSERR
RCVBSE:	CALL	ILPRT
DB	CR,LF,'++  Bad sector # in Hdr',CR,LF,0
	JMP	RCVSERR

RCVDATA:
	MOV	A,D
	STA	RCVSNO
	MVI	A,1
	STA	DATAFLG
	MVI	C,0
	CALL	CLRCRC	;clear crc counter
	LXI H,80H
RCVCHR:	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	CALL	RCVERR	;CHECK FOR RECEIVE ERROR
	JC	RCVDERR
	MOV	M,A
	INR	L
	JNZ	RCVCHR
	LDA	CRCFLG
	ORA	A
	JZ	RCVCRC
	MOV	D,C
	XRA	A
	STA	DATAFLG
	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	CALL	RCVERR	;CHECK FOR RECEIVE ERROR
	JC	RCVDERR
	CMP	D
	JNZ	RCVCERR
CHKSNUM:
	LDA	RCVSNO
	MOV	B,A
	LDA	SECTNO
	CMP	B
	JZ	RECVACK
	INR	A
	CMP	B
	JNZ	ABORT
	RET
;
RCVCRC:	MVI	E,2	;nr of crc bytes
	;
RCVCRC2:
	MVI	B,1
	CALL	RECV
	JC	RCVSTOT
	CALL	RCVERR
	JC	RCVDERR
	DCR	E
	JNZ	RCVCRC2
	CALL	CHKCRC
	ORA	A
	JZ	CHKSNUM
	LDA	VSEEFLG
	ORA	A
	JZ	RCVCRER
	LDA	QFLG
	ORA	A
	JZ	RCVSERR
	;
RCVCRER:
	CALL	ILPRT
DB	CR,LF,'++CRC error++',0
	JMP	RCVPRN

RCVCERR:
	LDA	VSEEFLG
	ORA	A
	JZ	RCVCPR
	LDA	QFLG
	ORA	A
	JZ	RCVSERR
RCVCPR:	CALL	ILPRT
DB	'++Cksum error++ ',0
	JMP	RCVPRN

RECVACK:
	CALL	SENDACK
	JMP	RCVSECT

SENDACK:
	MVI	A,ACK
	CALL	SEND
	RET

SENDHDR:
	LDA	QFLG
	ORA	A
	JZ	SENDHNM
	CALL	ILPRT
DB	CR,'Send # ',0
	PUSH	H
	LHLD	SECTNO	;GET SECTOR NUMBER
	CALL	DECOUT	;PRINT IT IN DECIMAL
	CALL	ILPRT
	DB	'  (',0
	CALL	DHXOUT	;16 BIT HEX CONVERSION & OUTPUT
	CALL	ILPRT
	DB	'H)',0
	POP	H
SENDHNM:
	MVI	A,SOH
	CALL	SEND
	LDA	SECTNO
	CALL	SEND
	LDA	SECTNO
	CMA
	CALL	SEND
	RET

SENDSEC:
	MVI	A,1
	STA	DATAFLG
	MVI	C,0
	CALL	CLRCRC
	LXI	H,80H
SENDC:	MOV	A,M
	CALL	SEND
	INR	L
	JNZ	SENDC
	XRA	A
	STA	DATAFLG
	RET

SENDCKS:
	MOV	A,C
	CALL	SEND
	RET

SENDCRC:
	CALL	FINCRC
	MOV	A,D
	CALL	SEND
	MOV	A,E
	CALL	SEND
	XRA	A
	RET

GETACK:	MVI	B,7	;10 IN ORIG PROG
	CALL	RECVDG
	JC	GETATOT
	CPI	ACK
	RZ
	MOV	B,A
	LDA	QFLG
	ORA	A
	JZ	ACKERR
	MOV	A,B
	CALL	CRLF
	CALL	HEXO
	CALL	ILPRT
DB	'H Recv''d, not ACK',CR,LF,0
ACKERR:	LDA	ERRCT
	INR	A
	STA	ERRCT
	CPI	ERRLIM
	RC
	LDA	VSEEFLG
	ORA	A
	JZ	GACKV
	LDA	QFLG
	ORA	A
	JZ	CSABORT
GACKV:	CALL	CKQUIT
	STC
	RZ
CSABORT:
	CALL	ERXIT
DB	CR,LF,'Can''t send sector -- Aborting',CR,LF,'$'
	;
GETATOT:
	LDA	QFLG
	ORA	A
	JZ	ACKERR
	CALL	ILPRT
DB	CR,LF,'Timeout on ACK',CR,LF,0
	JMP ACKERR

CKABORT:
	LDA	VSEEFLG
	ORA	A
	JZ	CKABGO
	LDA	QFLG
	ORA	A
	RZ	
CKABGO:	CALL	STAT
	RZ
	CALL	KEYIN
	CPI	CAN
	RNZ
ABORT:	LXI	SP,STACK
ABORTL:	MVI	B,1
	CALL	RECV
	JNC	ABORTL
	MVI	A,CAN
	CALL	SEND
ABORTW:	MVI	B,1
	CALL	RECV
	JNC	ABORTW
	MVI	A,' '
	CALL	SEND
	CALL	ILPRT
DB	CR,LF,'Routine cancelled',CR,LF,BELL,0
	MVI	A,'B'	;TURN MULTI-FILE MODE..
	STA	BATCHFLG ;..OFF SO ROUTINE ENDS.
	JMP	DONETCE

INCRSNO:
	PUSH	H
	LHLD	SECTNO	;GET SECTOR NUMBER
	INX	H	;BUMP IT
	SHLD	SECTNO	;STORE IT
	MOV	A,L
	POP	H
	RET

ERASFIL:
	LDA	BATCHFLG ;DON'T ASK FOR ERASE..
	ORA	A	;..IN MULTI-FILE MODE,..
	JZ	NOASK	;..JUST DO IT.
	LXI	D,FCB
	MVI	C,SRCHF
	CALL	BDOS
	INR	A
	RZ
	CALL	ILPRT
DB	'File exists -- Type ''Y'' to erase: ',BELL,0
	CALL	KEYIN
	PUSH	PSW
	CALL	TYPE
	POP	PSW
	CALL	UCASE
	CPI	'Y'
	JNZ	MENU
	CALL	CRLF
	;
NOASK:	LXI	D,FCB
	MVI	C,ERASE
	CALL	BDOS
	RET

BLKFILE:
	CALL	ILPRT	;ROUTINE IF NO FILE IS NAMED FOR "SEND" OR "RECEIVE"
DB	CR,LF,'No file specified',CR,LF,BELL,0
	JMP	MENU

MAKEFIL:
	LXI	D,FCB
	MVI	C,MAKE
	CALL	BDOS
	INR	A
	RNZ
	CALL	ERXIT
DB	'Error - Can''t make file',CR,LF
DB	'Directory must be full',CR,LF,'$'

	IF	CPM2X
CNREC:	MVI	C,FILSIZ ;COMPUTE FILE SIZE FUNCTION IN CP/M 2.x
	LXI	D,FCB	;POINT TO FILE CONTROL BLOCK
	CALL	BDOS
	LHLD	FCB+33	;GET RECORD COUNT
	SHLD	RCNT	;STORE IT
	LXI	H,0	;ZERO HL
	SHLD	FCB+33	;RESET RANDOM RECORD IN FCB
	RET
	ENDIF

	IF	NOT CPM2X
CNREC:	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++$'

;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,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
	ENDIF

OPENFIL:
	XRA	A
	STA	FCBEXT
	LXI	D,FCB
	MVI	C,OPEN
	CALL	BDOS
	INR	A
	JNZ	OPENOK
	CALL	ERXIT
DB	'Can''t open file$'

OPENOK:	LDA	BATCHFLG
	ORA	A
	JNZ	OPENOK1
	LDA	QFLG
	ORA	A
	RZ
OPENOK1:
	CALL	ILPRT
DB	'File open, size: ',0
	LHLD	RCNT	;GET RECORD COUNT
	CALL	DECOUT	;PRINT NUMBER OF SECTORS IN DECIMAL
	CALL	ILPRT	;PRINT
	DB	' (',0
	CALL	DHXOUT
	CALL	ILPRT
	DB	'H) sectors',CR,LF,0
	RET

CLOSFIL:
	LXI	D,FCB
	MVI	C,CLOSE
	CALL	BDOS
	INR	A
	RNZ
	CALL	ERXIT
DB	'Can''t close file$'

RDSECT:
	LDA	SECINBF
	DCR	A
	STA	SECINBF
	JM	RDBLOCK
	LHLD	SECPTR
	LXI	D,80H
	CALL	MOVE128
	SHLD	SECPTR
	RET

RDBLOCK:
	LDA	EOFLG
	CPI	1
	STC
	RZ
	MVI	C,0
	LXI	D,DBUF
RDSECLP:
	PUSH B
	PUSH D
	MVI	C,STDMA
	CALL	BDOS
	LXI	D,FCB
	MVI	C,READ
	CALL	BDOS
	POP	D
	POP	B
	ORA	A
	JZ	RDSECOK
	DCR	A
	JZ	REOF
	CALL	ERXIT
DB	'++	File read error	++$'
RDSECOK:
	LXI	H,80H
	DAD	D
	XCHG
	INR	C
	MOV	A,C
	CPI	DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS
	JZ	RDBFULL
	JMP	RDSECLP
REOF:	MVI	A,1
	STA	EOFLG
	MOV	A,C
RDBFULL:
	STA	SECINBF
	LXI	H,DBUF
	SHLD	SECPTR
	LXI	D,80H
	MVI	C,STDMA
	CALL	BDOS
	JMP	RDSECT
;
WRSECT:	LHLD	SECPTR
	XCHG
	LXI	H,80H
	CALL	MOVE128
	XCHG
	SHLD	SECPTR
	LDA	SECINBF
	INR	A
	STA	SECINBF
	CPI	DBUFSIZ*8 ;BUFFER SIZE IN 128 BYTE SECTORS
	RNZ
;
WRBLOCK:
	LDA	SECINBF
	ORA	A
	RZ
	MOV	C,A
	LXI	D,DBUF
DKWRLP:	PUSH	H
	PUSH	D
	PUSH	B
	MVI	C,STDMA
	CALL	BDOS
	LXI	D,FCB
	MVI	C,WRITE
	CALL	BDOS
	POP	B
	POP	D
	POP	H
	ORA	A
	JNZ	WRERR
	LXI	H,80H
	DAD	D
	XCHG
	DCR	C
	JNZ	DKWRLP
	XRA	A
	STA	SECINBF
	LXI	H,DBUF
	SHLD	SECPTR
	RET

WRERR:	MVI	C,CAN
	CALL	SEND
	CALL	ERXIT
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 $
	CALL	IN$MODDATP
	CALL	IN$MODDATP
RECV:	PUSH	D
	LDA	FASTCLK
	ORA	A
	JZ	MSEC
	MOV	A,B
	ADD	A
	MOV	B,A
MSEC:	LXI	D,15000	;60% OF ORIG 50000
	CALL	CKABORT
MWTI:	CALL	IN$MODCTLP
	CALL	ANI$MODRCVB
	CALL	CPI$MODRCVR
	JZ	MCHAR
	DCR	E
	JNZ	MWTI
	DCR	D
	JNZ	MWTI
	DCR	B
	JNZ	MSEC
	POP	D
	STC
	RET

MCHAR:	LDA	PMMIBYTE ;IS THE MODEM A PMMI?
	ORA	A	;SET FLAGS
	JZ	MCHAR1	;YES, JUMP
	CALL	IN$MODCTLP ;GET ERROR-STATUS BYTE
	ANI	ERRCDMSK ;MASK OUT ALL EXCEPT ERROR BITS (3-5)
	STA	ERRCDE	;SAVE THE ERROR CODE
MCHAR1:	CALL	IN$MODDATP
	POP	D
	PUSH	PSW
	CALL	UPDCRC	;calc crc
	ADD	C	
	MOV	C,A
	LDA	RSEEFLG
	ORA	A
	JZ	MONIN
	LDA	VSEEFLG
	ORA	A
	JNZ	NOMONIN
	LDA	DATAFLG
	ORA	A
	JZ	NOMONIN
MONIN:	POP	PSW
	PUSH	PSW
	CALL	SHOW
NOMONIN:
	POP	PSW
	ORA	A
	RET

SEND:	PUSH	PSW
	LDA	SSEEFLG
	ORA	A
	JZ	MONOUT
	LDA	VSEEFLG
	ORA	A
	JNZ	NOMONOT
	LDA	DATAFLG
	ORA	A
	JZ	NOMONOT
MONOUT:	POP	PSW
	PUSH	PSW
	CALL	SHOW
NOMONOT:
	POP	PSW
	PUSH	PSW
	CALl	UPDCRC	;calc crc
	ADD	C
	MOV	C,A
SENDW:	CALL	IN$MODCTLP
	CALL	ANI$MODSNDB
	CALL	CPI$MODSNDR
	JNZ	SENDW
	POP	PSW
	CALL	OUT$MODDATP
	RET

WAITNAK:
	LDA	VSEEFLG
	ORA	A
	JZ	WAITNPR
	LDA	QFLG
	ORA	A
	JZ	WAITNLP
WAITNPR:
	CALL	ILPRT
DB	'Awaiting initial NAK',CR,LF,0
WAITNLP:
	CALL	CKABORT
	MVI	B,1
	CALL	RECV
	CPI	NAK
	RZ
	CPI	CRC	;crc request?
	JZ	WAITCRC	;yes, go set crc flag
	DCR	E
	JZ	ABORT
	JMP	WAITNLP
;
WAITCRC:
	CALL	ILPRT
DB	'CRC request received',CR,LF,0
	XRA	A
	STA	CRCFLG
	RET
;
;--->PARITY: Routine to setup PMMI for odd/even parity.

PARITY:	LDA	PMMIBYTE ;IS MODEM A PMMI?
	ORA	A	;SET FLAGS
	RZ		;NO, RETURN
	LDA	OPARITY	;GET ODD PARITY REQUEST BYTE
	ORA	A	;SET FLAGS
	JNZ	EVENPAR	;IF NOT ODD SEE IF IT IS EVEN
	LDA	UARTCTLB ;GET UART/MODEM CONTROL BYTE
	ANI	ODPARMSK
	JMP	PARITY1

EVENPAR:
	LDA	EPARITY	;GET EVEN PARITY REQUEST BYTE
	ORA	A	;SET FLAGS
	RNZ		;IF EVEN PARITY NOT SPECIFIED RETURN
	LDA	UARTCTLB ;GET UART/MODEM CONTROL BYTE
	ANI	ODPARMSK ;SET FOR PARITY
	ORI	EVPARMSK ;NOW SET FOR EVEN PARITY

PARITY1:
	JMP	OUT$MODCTLP ;SEND TO PMMI -
			;WHEN OUT$MODCTLP DOES RET IT
			;WILL GO BACK TO CALLING ROUTINE

NOPARIT:
	LDA	PMMIBYTE
	ORA	A
	RZ
	LDA	UARTCTLB	;GET UART/MODEM CONTROL BYTE
	ORI	NOPARMSK	;RESET PARITY BIT ON PMMI
	JMP	OUT$MODCTLP


INITADR:
	LHLD	1
	LXI	D,3
	DAD	D
	SHLD	VSTAT+1
	DAD	D
	SHLD	VKEYIN+1
	DAD	D
	SHLD	VTYPE+1
	LDA	PMMIBYTE
	ORA	A
	JZ	JMP$INITMOD	;RETURN DONE FROM THIS ROUTINE..
	LDA	IN$MODCTLP+1	;..IF NOT PMMI
	STA	OUT$MODCTLP+1
	INR	A
	STA	OUT$MODDATP+1
	STA	IN$MODDATP+1
	INR	A
	STA	IN$BAUDRP+1
	STA	OUT$BAUDRP+1
	INR	A
	STA	OUT$MODCTL2+1
	RET

PROCOPT:
	LXI	D,FCB+1
	LDAX	D
	STA	OPTION
OPTLP:	INX	D
	LDAX	D
	CPI	' '
	JZ	ENDOPT
	LXI	H,OPTBL
	MVI	B,OPTBE-OPTBL
OPTCK:	CMP	M
	JNZ	OPTNO
	CPI	'O'	; @7.61
	JNZ	OPTCK1	; @7.61
	XRA	A	; @7.61
	STA	UARTFLG	; @7.61
	JMP	OPTCK2	; @7.61
OPTCK1:	CPI	'A'	; @7.61
	JNZ	OPTCK2	; @7.61
	MVI	A,0FFH	; @7.61
	STA	UARTFLG	; @7.61
OPTCK2:	MVI	M,0	; @7.61
	JMP	OPTLP
OPTNO:	INX	H
	DCR	B
	JNZ	OPTCK
	JMP	BADOPT

ENDOPT: LDA	CRCFLG
	ORA	A
	JNZ	ENDOPT2
	LDA	OPTION
	CPI	'R'
	JNZ	BADOPT	;crc only allowed for recv
;
ENDOPT2:
	LDA	VSEEFLG
	ORA	A
	RNZ
	STA	QFLG
	RET

DONE:	LDA	BATCHFLG
	ORA	A
	JNZ	DONETCC
	LDA	QFLG
	ORA	A
	JZ	NMSTRNS
	LXI	H,FCB+1	;PUT FILE NAME IN..
	LXI	D,FTRNMSG ;..SPACES IN MESSAGE..
	MVI	B,8	;..BELOW.
	CALL	MOVE
	INX	D	;PUT FILE TYPE AFTER..
	MVI	B,3	;..SKIPPING ONE SPACE..
	CALL	MOVE	;..BELOW.	
	CALL	ILPRT
FTRNMSG:
DB	'              transferred',CR,LF,CR,LF,0	;13 SPACES

NMSTRNS:
	LDA	FCB	;SAVE DRIVE NO.
	STA	DISKNO
	LXI	H,FCB	;BLANK OUT FILE CONTROL BLOCKS
	CALL	INITFCBS
	LDA	DISKNO	;PUT DRIVE NUMBER BACK
	STA	FCB
	LXI	H,RESTSN ;RESTORE SECTORE NUMBERS..
	LXI	D,SECTNOB ;..FOR NEW FILE TRANSFER.
	MVI	B,SECTNOE-SECTNOB ;ROUTINE ALSO DONE IN MENU.
	CALL	MOVE
	LDA	SENDFLG	;GOES TO EITHER SEND OR..
	ORA	A	;..RECEIVE FILE, DEPENDING..
	JNZ	SENDFIL	;..UPON WHICH ROUTINE SET..
	JMP	RCVFIL1	;..THE FLAG IN MULTI-FILE MODE. @FIX7.61

DONETCC:
	MVI	A,TRUE	;INDICATE NO FILES BEING..
	STA	FSTFLG	;RESET MULTIFILE TRANS
	STA	NFILFLG	;..USED IN TERMINAL ROUTINE.
	CMA
	OUT	FRONTPAN
	STA	SAVEFLG	;STOP MEMORY SAVE IN TERM ROUTINE.
	LDA	VSEEFLG
	ORA	A
	JZ	DONETC
	LDA	QFLG
	ORA	A
	JZ	DONETCA
DONETC:	CALL	ILPRT
DB	CR,LF,'All transfers completed'
DB	CR,LF,BELL,0
DONETCA:
	LDA	DISCFLG	;see if disconnect when thru
	ORA	A
	JNZ	DONETCE	;no, don't disconnect
DONETCB:
	CALL	ILPRT
DB	CR,LF,'++Press RETURN to disconnect++',BELL,CR,LF,0
	MVI	C,RDCON
	CALL	BDOS	;wait for response
	CPI	0DH	;carriage return
	JNZ	DONETCB	;nope
	CALL	ILPRT
DB	CR,LF,'++Disconnected++',CR,LF,0
	CALL	DISCONNT ;hang-up the pmmi
	JMP	EXIT	;go to CP/M

DONETCE:
	CALL	NOPARIT	;RESET TO NO PARITY
	mvi	a,crc	;@7.41....................
	sta	CRCFLG	;turn off CRC option
	mvi	a,0FFH
	sta	FIRSTME	;set first-time flag
			;@7.41....................
	LDA	TERMFLG	;SEE IF RETURN TO..
	ORA	A	;..TERMINAL MODE..
	JNZ	MENU	;..AFTER X'FER.
	CALL	CRLF
	JMP	TERM

INITMOD:
SETBAUD:
	LDA	PMMIBYTE
	ORA	A
	RZ
	LDA	ANSWFLG	;IF ANSWER OR ORIGINATE MODE..
	ORA	A	;..IS NOT REQUESTED OR NO..
	JNZ	SKIPB1	;..BAUDRATE SPECIFIED, THEN..
	CALL	GETBAUD	;..ROUTINE RETURNS WITH CHANGE..
	JMP	FIXBAUD	;..OF BAUD. IF OPT REQUESTED,..
SKIPB1:	LDA	ORIGFLG	;..A BLANK FORCES 300 BAUD..
	ORA	A	;..ELSE A 0 FROM NEWBAUD..
	RNZ		;..FORCES 300 BAUD.
	CALL	GETBAUD
FIXBAUD:
	CALL	OUT$BAUDRP
	CPI	52
	MVI	A,5FH
	JC	GT300
	MVI	A,7FH
GT300:	CALL	OUT$MODCTL2
	STA	MODCTLB	;SAVE MODEM CONTROL BYTE
	LDA	ORIGFLG
	ORA	A
	MVI	A,ORIGMOD
	JZ	OFFHOOK
	LDA	ANSWFLG
	ORA	A
	MVI	A,ANSWMOD
	RNZ

OFFHOOK:
	LXI	H,4000
OFFDLY:	DCR	L
	JNZ	OFFDLY
	DCR	H
	JNZ	OFFDLY
	CALL	OUT$MODCTLP
	RET

GETBAUD:
	LDA	FCB+9
	CPI	' '
	MVI	A,52
	RZ
	LDA	FCB+9
	CPI	0
	MVI	A,52
	RZ

	LXI	D,FCB+9
	LXI	H,0
DECLP:	LDAX	D
	INX	D
	CPI	' '
	JZ	DECLP
	CPI	'0'
	JC	BADRATE
	CPI	'9'+1
	JNC	BADRATE
	SUI	'0'
	MOV	B,H
	MOV	C,L
	DAD	H
	DAD	H
	DAD	B
	DAD	H
	ADD	L
	MOV	L,A
	JNZ	DIGNC
	INR	H
DIGNC:	MOV	A,E
	CPI	FCB+12
	JNZ	DECLP
	MOV	A,H
	CMA
	MOV	D,A
	MOV	A,L
	CMA
	MOV	E,A
	INX	D
	LXI	H,15625
	LXI	B,-1
DIVLP:	INX	B
	DAD	D
	JC	DIVLP
	MOV	A,B
	ORA	A
	MOV	A,C
	RZ

BADRATE:
	CALL	ERXIT
DB	'++	Invalid baud rate ++$'

MOVEFCB:
	LXI	H,FCB+16
	LXI	D,FCB
	MVI	B,16
	CALL	MOVE
	XRA	A
	STA	FCBSNO
	STA	FCBEXT
	RET

SHOW:	CPI	LF
	JZ	CTYPE
	CPI	CR
	JZ	CTYPE
	CPI	9
	JZ	CTYPE
	CPI	' '
	JC	SHOWHEX
	CPI	7FH
	JC	CTYPE
SHOWHEX:
	PUSH	PSW
	MVI	A,'('
	CALL	CTYPE
	POP	PSW
	CALL	HEXO
	MVI	A,')'
	JMP	CTYPE

CTYPE:	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,A
	MVI	C,WRCON
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	RET

CRLF:	PUSH	PSW
	MVI	A,CR
	CALL	TYPE
	MVI	A,LF
	CALL	TYPE
	POP	PSW
	RET

TYPE:	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A
VTYPE:	CALL	$-$
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET

STAT:	PUSH	B
	PUSH	D
	PUSH	H
VSTAT:	CALL	$-$
	POP	H
	POP	D
	POP	B
	ORA	A
	RET

KEYIN:	PUSH	B
	PUSH	D
	PUSH	H
VKEYIN:	CALL	$-$
	POP	H
	POP	D
	POP	B
	RET

UCASE:	CPI	61H	;CHANGES LOWER CASE CHARACTER..
	RC		;..IN A-REG TO UPPER CASE.
	CPI	7BH
	RNC
	ANI	5FH
	RET

DECOUT:	PUSH	PSW
	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
	POP	PSW
	RET

;---->	DHXOUT: - double precision hex output routine.

DHXOUT:	PUSH	H
	PUSH	PSW
	MOV	A,H	;GET MS BYTE
	CALL	HEXO	;OUTPUT HIGH ORDER BYTE
	MOV	A,L	;GET LS BYTE
	CALL	HEXO	;OUTPUT LOW ORDER BYTE
	POP	PSW
	POP	H
	RET

HEXO:	PUSH	PSW
	RAR
	RAR
	RAR
	RAR
	CALL	NIBBL
	POP	PSW
NIBBL:	ANI	0FH
	CPI	10
	JC	ISNUM
	ADI	7
ISNUM:	ADI	'0'
	JMP	TYPE

;RETURNS W/ ZERO SET IF RETRY ASKED. IF MULTI-FILE MODE, THEN
;NO QUESTIONS ASKED, JUST QUIT

CKQUIT:	LDA	BATCHFLG
	ORA	A
	JNZ	CKQTASK	;ASK FOR RETRY
	INR	A	;RESET ZERO FLG
	RET
CKQTASK:
	XRA	A
	STA	ERRCT
	CALL	ILPRT
DB	'Multiple errors encountered.',CR,LF
DB	'Type Q to quit, R to retry:  ',BELL,0
	CALL	KEYIN
	PUSH	PSW
	CALL	CRLF
	POP	PSW
	CALL	UCASE	;INSTEAD OF "ANI 5FH"
	CPI	'R'
	RZ
	CPI	'Q'
	JNZ	CKQUIT
	ORA	A
	RET

ILPRT:	XTHL
ILPLP:	MOV	A,M
	ORA	A
	JZ	ILPRET
	CALL	CTYPE
	INX	H
	JMP	ILPLP
ILPRET:	XTHL
	RET

PRTMSG:	MVI	C,PRINT
	JMP	BDOS

ERXIT:	POP	D
	CALL	PRTMSG
	CALL	ILPRT
DB	BELL,0
	LDA	BATCHFLG
	ORA	A
	JNZ	DONETCE
	MVI	A,'Q'	;RESET QFLG
	STA	QFLG
	JMP	ABORT	;ABORT OTHER COMPUTER

EXIT:	LXI	D,80H
	MVI	C,STDMA
	CALL	BDOS
	LHLD	STACK
	SPHL
	LDA	SAVCCP
	ORA	A
	JZ	0	;WARM BOOT
	RET

MOVE128:
	MVI	B,128
MOVE:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	MOVE
	RET

;DIALING ROUTINES TAKEN (AND GREATLY MODIFIED) FROM PMMI MANUAL.

;MODEM CONTROL COMMAND WORDS

CLEAR	EQU 3FH	;IDLE MODE
MAKEM	EQU 1	;TELE LINE MAKE (OFF HOOK)
BRKM	EQU 0	;TELE LINE ON HOOK (BREAK DURING DIALING)
DTMSK	EQU 1	;DIAL TONE MASK
RBLMT	EQU 70	;# OF SEC*10 TO WAIT BEFORE GIVING NO RING HEARG MSG
RBWAIT	EQU 50	;# OF SEC*10 DELAY BEFORE REDIALING NUMBER
TMPUL	EQU 80H	;TIMER PULSES MASK BIT
TRATE	EQU 250	;VALUE FOR 0.1 SECOND


DIALPL:	LDA	PMMIBYTE ;FLAG FOR PMMI OPERATION
	ORA	A	;SET FLAGS
	RZ		;PMMI FALSE, RETURN
	XRA	A	; 0
	STA	CRFLAG	;CONTINUOUS REDIAL FLAG
	CALL	DIALPL0	; DISCONNECT, RECONNECT, WAIT DIAL TONE
	JC	DILAGN	;ASK IF TRY AGAIN
	LXI	H,CMDBUF+1 ;POINT # OF CHARS IN BUFF
	MOV	A,M	;GET # OF CHARS
	CPI	4	;4 OR MORE CHARS TYPED BEFORE <CR>?
	JC	ENTNUM	;NO, ASK FOR NUMBER
	LXI	H,CMDBUF+6 ;POINT TO NUMBER TO DIAL
	JMP	DIAL10	;CHECK IF LIB #, & DIAL

DIALPL0:
	CALL	DISCONNT
	CALL	ILPRT
DB	CR,LF,'Waiting for dial tone',CR,LF,0

	MVI	A,MAKEM	;MAKE MAKE (OFF-HOOK)
	CALL	OUT$MODCTLP ;DO IT
	MVI	D,DTMSK	;DIAL TONE MASK
	MVI	C,100	;10 SECOND WAIT
	CALL	WAIT	;WAIT FOR DIAL TONE
	NOP		;DELAY

; WAIT SUBROUTINE WILL RETURN WITH CARRY SET IF UNABLE TO
; GET DIALTONE, ELSE CARRY NOT SET MEANS DIALTONE RECEIVED

	RNC		;IF DIAL TONE WITHIN 10 SECONDS
	CALL	ILPRT	;ELSE, MESSAGE AND RETURN WITH CARRY SET
DB	CR,LF
DB	'++No dial tone after 10 seconds++',CR,LF,0
	STC
	RET

ENTNUM:	;this is all the set-up for the print at entnum2.
	MVI	C,13	;number of lines to move
	LXI	H,NUMBLIB ;address of source memory
	LXI	D,DBUF	;address of target memory
	CALL	NEWLINE	;start with CRLF
	STAX	D	;+LF
	INX	D	;and bump it

ENTNUM1:
	MVI	B,30	;number of bytes to move
	CALL	MOVE	;move to buffer
	CALL	SPACES	;2 entries + 3 spaces = 63 characters
	MVI	B,30
	CALL	MOVE
	CALL	NEWLINE
	DCR	C	;number of lines to print
	JZ	ENTNUM2
	JMP	ENTNUM1

NEWLINE:		;puts CR-LF at memory pointed by DE
	MVI	A,CR	;CR
	STAX	D	;store it
	MVI	A,LF	;LF
	INX	D	;bump pointer
	STAX	D	;store LF
	INX	D	;bump pointer
	RET

SPACES:
	MVI	A,20H	;space
	STAX D ! INX D	; 1
	STAX D ! INX D	; 2
	STAX D ! INX D	; 3
	RET

ENTNUM2:
	MVI	A,'$'
	STAX	D
	MVI	C,PRINT
	LXI	D,DBUF	;point to table of numbers to print
	CALL	BDOS
	CALL	CRLF

	CALL	ILPRT
DB	'Enter number or library letter - Type C/R when finished,',CR,LF
DB	'CTRL-X cancels while dialing:        ',0

	LXI	D,CMDBUF
	CALL	INBUFF

DIALLP1:
	LDA	CMDBUF+1
	ORA	A	;NULL MEANS <CR> WAS TYPED
	JZ	BORTIT	;ABORT DIALING, RETURN TO MENU

	LXI	H,CMDBUF+2 ;FIRST TYPED CHAR OF NUMBER TO DIAL
 ;
 ; ENTER THIS ROUTINE WITH HL POINTING TO NUMBER TO DIAL
 ;
DIAL10:
	MVI	B,'A'	;FIRST LETTER OF ALPHABET
	MVI	E,0	;COUNTS NUMBER OF LETTERS TO MATCH
	MVI	C,26	;NUMBER OF LETTERS IN ALPHABET
	MOV	A,M	;GET CHAR BUFFER
DIAL11:
	CMP	B	;NUMBER FROM TABLE?
	JZ	LIBSET
	INR	B	;MAKE NEXT LETTER (A-Z)
	INR	E	;COUNT UP
	DCR	C	;COUNT DOWN
	JZ	DIALLPX	;NOT A LETTER
	JMP	DIAL11	;LOOP

LIBSET:
	LXI	H,NUMBLIB ;PHONE NUMBER LIBRARY
	LXI	B,30	;LENGTH OF LIBRARY ENTRY
	MOV	A,E	;NUMBER OF TIMES TO ADD 30 TO HL
	ORA	A	;SET FLAGS
	JZ	DIAL13

DIAL12:
	MOV	A,M	;GET FIRST CHAR OF SELECTED LIB ENTRY
	ORA	A	;SET FLAGS
	JZ	DIALLP2	;SEND BADLIB MSG
	DAD	B	;INCREMENT HL BY 30
	DCR	E	;COUNTDOWN
	JNZ	DIAL12	;NOT THERE YET, LOOP

DIAL13:
	MVI	B,30	;NUMBER OF CHARACTERS TO GET FROM TABLE
	LXI	D,CMDBUF+1 ;POINT TO BUFFER
	XCHG		;HL POINTS TO CMDBUF+1
	MOV	M,B	;STORE # OF BYTES IN A TABLE ENTRY
	XCHG		;RESTORE REG.
	INX	D	;POINT TO FIRST CHAR POSITION IN BUFFER
	CALL	MOVE	;MOVE TABLE ENTRY TO BUFFER

DIALLPX:
	LDA	CMDBUF+1
	MOV	E,A	;NUMBER OF CHARS IN BUFF
	LXI	H,CMDBUF+2 ;POINT FIRST CHAR

DIALLP2:
	MOV	A,M	;GET FIRST # FROM BUFFER
 ;
 ; ROUTINE TO PRINT 'BADLIB' MESSAGE AND ABORT IF NULL ENCOUNTERED
 ;
	ORA	A	;SET FLAGS
	PUSH	D	;SAVE DE REGISTERS
	LXI	D,BADLIB ;BAD LIBRARY NUMBER IF NULL
	MVI	C,PRINT	; 9
	PUSH	PSW	;SAVE A AND FLAGS
	CZ	BDOS
	POP	PSW	;RESTORE A AND FLAGS
	POP	D	;RESTORE DE REGISTERS
	JZ	BORTIT	;ABORT
;
; DIAL A DIGIT, CHECK KBD FOR ABORT
;
	CALL	DIAL	;DIAL IT
	CALL	STAT	; KEYPRESS?
	ORA	A	;SET FLAGS
	CNZ	KEYIN	;YES, GO GET IT
	CPI	CAN	; ^X?
	JZ	BORTIT	;YES, ABORT
	INX	H	;BUMP POINTER
	PUSH	D	;SAVE DE
	PUSH	H	;SAVE HL
	MVI	B,1	;WAIT 1 TIME INTERVAL
	CALL	TIMER
	POP	H	;RESTORE HL
	POP	D	;RESTORE DE
	DCR	E	;COUNT DOWN CHARS IN BUFF
	JNZ	DIALLP2	;NOT DONE, LOOP
	JZ	DIALDN	;DIALING DONE

DISCONNT:
	XRA	A	;0
	CALL	OUT$MODCTL2 ;CLEAR DAV, ESD, ETC
	CALL	OUT$MODCTLP ;HANG-UP
	PUSH	B
	MVI	B,8	;wait for PMMI to disconnect
	CALL	TIMER
	POP	B
	RET

TIMER:	MVI	A,TRATE	;TRATE 250, VALUE FOR .1 SEC INTERVAL
	CALL	OUT$BAUDRP ;B-REG CONTAINS NUMBER OF .1 SEC INTERVALS
TIMES:	CALL	IN$BAUDRP  ;TO COUNT
	ANI	TMPUL
	JZ	TIMES	;WAIT FOR TIMER TO GO HIGH
TIMEE:	CALL	IN$BAUDRP
	ANI	TMPUL
	JNZ	TIMEE	;WAIT FOR TIMER TO GO LOW
	DCR	B
	JNZ	TIMES
	RET

BORTIT:	CALL DISCONNT
	JMP	MENU

;AUTO DIALER

DIAL:	CALL	TYPE	;PRINT WHATEVER CHARACTER, DASHES, ETC.
	CPI	30H
	RC		;DIGIT MUST BE AT LEAST 0..
	CPI	'R'	;COULD IT BE A RINGBACK CHARACTER
	JNZ	DIAL1	;NO? - JUMP
	PUSH	PSW	;SAVE ACCUMULATOR & FLAGS
	MOV	A,E	;GET # OF CHAR LEFT INTO ACC.
	CPI	01H	;IS THIS THE LAST CHARACTER?
	JZ	RINGBK	;IF SO, IT MUST BE RINGBACK CHAR - DO RINGBACK
	POP	PSW	;EVERYTHING BACK AS IT WAS
DIAL1:	CPI	3AH
	RNC		;..AND NOT MORE THAN 9
	ANI	0FH	;STRIP ASCII -- COULD ALSO DO SUI 30H ('0')
	JNZ	DIALS
	MVI	A,10	;CONVERT ZERO TO 10 PULSES
DIALS:	MOV	C,A
	LDA	PULSERATE ;CONTAINS VALUE FOR DIAL SPEED
	CALL	OUT$BAUDRP
DIALC:	CALL	IN$BAUDRP
	ANI	TMPUL
	JNZ	DIALC
DIALB:	CALL	IN$BAUDRP
	ANI	TMPUL
	JZ	DIALB
MAKEP:	MVI	A,MAKEM
	CALL	OUT$MODCTLP
TIMEM:	CALL	IN$BAUDRP
	ANI	TMPUL
	JNZ	TIMEM
	MVI	A,BRKM
	CALL	OUT$MODCTLP
TIMEB:	CALL	IN$BAUDRP
	ANI	TMPUL
	JZ	TIMEB
	DCR	C
	JNZ	MAKEP
	MVI	A,MAKEM
	CALL	OUT$MODCTLP
	MVI	B,2
	CALL	TIMER
	RET

RINGBK:	POP	PSW	;TO GET IT OFF THE STACK
	LDA	CMDBUF+1 ;GET # OF CHAR IN BUFFER
	SUI	01	;SUBTRACT 1 TO AVOID THE RINGBACK CHAR
	STA	CMDBUF+1 ;STORE THE NEW VALUE
	MVI	D,DTMSK	;LOAD TONE DETECT MASK
	MVI	C,RBLMT	;SET TIMER FOR RBLMT NUMBER OF SECONDS
	CALL	WAIT
	JC	RBTIME	;JUMP IF NO RING DETECTED
	MVI	B,25	;WAIT 2.5 SEC
	CALL	TIMER
	CALL	IN$BAUDRP ;IS TONE STILL PRESENT?
	ANA	D
	JNZ	RNGBK1
	JMP	DILAGN	;YES, MUST BE BUSY

RNGBK1:	CALL	HANGP	;HANG UP THE PHONE
	MVI	B,RBWAIT ;WAIT X SEC
	CALL	TIMER
	CALL	DIALPL0	;GO OFF HOOK & LISTEN FOR DIAL TONE
	JNC	DIALLPX	;GO REDIAL NUMBER
	JMP	DILAGN	;NO DIAL TONE HEARD

RBTIME:	CALL	CRLF
	JMP	RNGBK1	;HANGUP, REDIAL, & LISTEN FOR CARRIER


;TIME OUT ROUTINE. MUST BE CALLED WITH MASK IN D REG FOR INPUT
;AT RELATIVE PORT 2 AND NUMBER OF SECONDS * 10 IN C REG.

WAIT:	MVI	B,1	
	CALL	TIMER	;WAIT FOR TIMER TO GO HIGH THEN LOW
	CALL	IN$BAUDRP ;PMMIADDR+2 (MODEM STATUS PORT)
	ANA	D	;(CTS or DIALTONE MASK)
	RZ		;ACTIVE LOW, SO RETURN ON 0
	PUSH	B	;SAVE..
	PUSH	D	;..ACTIVE REG'S
	CALL	STAT	;KEYPRESS?
	ORA	A	;SET FLAGS
	CNZ	KEYIN	;YES, GET CHAR
	CPI	CAN	;^X?
	JZ	WAIT1	;YES, DISCONNECT, JMP TO MENU
	POP D		;RESTORE..
	POP B		;..REGS
	DCR C		;COUNT-DOWN
	JNZ	WAIT
	STC		;SET CARRY TO INDICATE MASK NOT SET
	RET

WAIT1:
	POP	D	;RESET..
	POP	B	;..STACK
	JMP	DISCON1	;DISCONNECT

HANGP:	MVI	A,CLEAR
	CALL	OUT$MODCTL2
	MVI	A,0
	CALL	OUT$MODCTLP
	RET

DIALDN:	CALL	CRLF
	MVI	A,07FH	    ;TURN ON DTR
	CALL	OUT$MODCTL2 ;TIMER RATE?

	MVI	B,1
	CALL	TIMER	;WAIT FOR MODEM TO TURN ON DTR

	MVI	A,5DH	;2 STOP BITS, NO PARITY, 8 DATA BITS
			;+ NO DISCONNECT AFTER 17 SECS
	CALL	OUT$MODCTLP

	MVI	D,4	  ;CLEAR TO SEND MASK
	MVI	C,WAITCTS ;wait time for cts (25.5 SEC MAX)
	CALL	WAIT

	JNC	CONMADE	;CONNECTION MADE

	CALL	DISCONNT
DILAGN:
	LDA	CRFLAG	;CONTINUOUS REDIAL FLAG
	ORA	A
	JNZ	DILAGN0
	CALL	ILPRT
DB	CR,LF,'No answer after time-out.  Redial? (Y/N/C): ',BELL,0
	CALL	KEYIN	;GET RESPONSE
	CALL	TYPE	;ECHO IT
	CALL	UCASE	;ANI 5FH
	CALL	CRLF	;NEW LINE
	CPI	'N'	;REDIAL?
	JZ	MENU	;NO, GO MENU
	CPI	'Y'	;REDIAL?
	JZ	DILAGN0	;YES, REDIAL
	CPI	'C'	;CONTINUOUS REDIAL?
	JNZ	DILAGN	;INVALID RESPONSE, ASK AGAIN
	XRA A ! CMA	;0FFH
	STA	CRFLAG	;CONTINUOUS REDIAL FLAG
 DILAGN0:
	MVI	B,50	;5 seconds wait for pmmi reset
	CALL	TIMER	;else busy tone may be sensed as dialtone
	CALL	DIALPL0	;WAIT FOR DIAL TONE
	JNC	DIALLP1	;DIAL NUMBER
	JMP	DILAGN	;NO DIAL TONE AFTER 10 SECS

CONMADE:
	CALL	ILPRT
DB	CR,LF,'Connection established - Select options: ',BELL,0
DILAGN1:
	CALL	STAT	;KEYPRESS?
	ORA	A	;SET FLAGS
	JNZ	GETCMD	;KEY PRESSED, GO GET OPTIONS
	MVI	A,BELL
	CALL	TYPE	;RING BELL
	LXI	B,2000H
DILAGN2:		;@7.32.........
	DCR	C
	JNZ	DILAGN2	;KILL SOME TIME FOR TERMINAL TO PROCESS BELL
	DCR	B
	JNZ	DILAGN2	;@7.32.........
	JMP	DILAGN1	;LOOP


;INITIALIZES CP/M FILE CONTROL BLOCKS AT 5CH AND 6CH
SETFCB:	LXI	D,CMDBUF
	LXI	H,FCB
	CALL	CPMLINE
	CALL	PROCOPT

CHECKNM:
	LDA	FCB+1	;CHECK ON THE PRIMARY OPTION
	CPI	'E'	;RETURN IF ECHO OPTION
	RZ
	CPI	'M'	;RETURN TO MENU
	RZ
	MOV	B,A
	LDA	PMMIBYTE
	ORA	A
	MOV	A,B
	JZ	S4
	CPI	'C'
	RZ
S4:	CPI	'T'
	JZ	TERMSEL
	CPI	'S'
	JZ	CKFILE
	CPI	'R'
	JNZ	BDOPT
	LDA	BATCHFLG ;IF MULT FILE MODE, THEN..
	ORA	A	 ;..RECV OPT DOES NOT NEED..
	RZ		 ;..NAME.
	JMP	CKFILE
BDOPT:	CALL	ILPRT
DB	CR,LF,'++Bad Option++',CR,LF,0
	JMP	REENT
CKFILE:	LDA	FCB+17	;IF OPTION THAT NEEDS FILE NAME,..
	CPI	' '	;..THEN CHECK TO SEE IF NAME..
	RNZ		;..EXISTS. IF NOT..
REENT:	CALL	ILPRT	;..DO EVERYTHING OVER.
DB	CR,LF,'Re-enter PRIMARY option and file name only: ',BELL,0
	LXI	D,CMDBUF
	CALL	INBUFF
	JMP	SETFCB

TERMSEL:
	LDA	FCB+17
	CPI	' '
	JNZ	SAVAGN
	MVI	A,FALSE
	STA	SAVEFLG
	MVI	A,TRUE
	STA	NFILFLG
	CMA
	OUT	FRONTPAN
	RET
SAVAGN:
	MVI	A,FALSE
	STA	NFILFLG
	RET

NEWBAUD:
	LDA	PMMIBYTE
	ORA	A
	RZ
	CALL	ILPRT
DB	CR,LF,'Enter New Baudrate: ',0	; @7.61
	LXI	H,FCB+9
	MVI	M,0	;PUTS A ZERO IN FIRST POSITION SO AS TO
LOOP5:	CALL	KEYIN	;FORCE THE DEFAULT OPTION OF 300 BAUD.
	CPI	CR	;CARRIAGE RET ENTERS BAUD RATE
	JNZ	CONNEWB	;GOES TO THE ESTABLISHED ROUTINE - RETURN TO MAIN
	CALL	CRLF	;PROGRAM IS DONE THERE.
	JMP	SETBAUD
CONNEWB:
	CPI	30H	;MAKE SURE IT'S..
	JC	LOOP5	;..A DIGIT, ELSE..
	CPI	3AH	;..DON'T ACCEPT IT.
	JNC	LOOP5
	MOV	M,A
	MOV	C,A
	CALL	TYPE	;ECHO THE CHARACTER ENTERED
	INX	H
	JMP	LOOP5
;
;****************************************************************
;* 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)		*
;*								*
;* 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.	*
;*								*
;****************************************************************
;
;	ENTRY	CLRCRC,UPDCRC,FINCRC,CHKCRC
;
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
;
MENU:	LXI	H,RESTSN  ;RESTORE SECTORE NUMBERS..
	LXI	D,SECTNOB ;..FOR NEW FILE TRANSFER.
	MVI	B,SECTNOE-SECTNOB
	CALL	MOVE
	LXI	H,RESTROPT ;RESTORE OPTION TABLE
	LXI	D,OPTBL
	MVI	B,OPTBE-OPTBL
	CALL	MOVE
	MVI	A,0
	STA	MFFLG1	;RESET MFACCESS ROUTINE..
	CMA		;..AND MULTI TRANS IN CASE..
	STA	FSTFLG	;..OF ABORT.

MENU1:	LDA	XPRFLG	;TEST IF MENU SHOULD BE SHOWN
	ORA	A
	JNZ	XPRT
MENU2:	CALL	ILPRT
DB	CR,LF,CR,LF
DB	'WRT   - Write file to disk (from terminal mode)',CR,LF
DB	'DEL   - Erase present file (from terminal mode)',CR,LF
DB	'RET   - Return to terminal mode with no loss of data',CR,LF,0
	LDA	PMMIBYTE
	ORA	A
	JZ	S5
	CALL	ILPRT
DB	'DSC   - Disconnect',CR,LF
DB	'BYE   - Disconnect and reboot',CR,LF
DB	'CAL   - Dial number',CR,LF,0
S5:	CALL	ILPRT
DB	'XPR   - Toggle expert mode (Menu on/off)',CR,LF
DB	'DIR   - List directory (may specify drive)',CR,LF
DB	'CPM   - Exit to CP/M',CR,LF
DB	'S     - Send CP/M file',CR,LF
DB	'R     - Receive CP/M file',CR,LF
DB	'T     - Terminal mode (optional file name)',CR,LF
DB	'E     - Terminal mode with echo',CR,LF,0
XPRT:	CALL	ILPRT
DB	CR,LF,CR,LF,'DEFAULT DRIVE: ',0
	MVI	C,25	;CURRENT DISK FUNCTION
	CALL	BDOS
	ADI	41H	;MAKE ASCII
	CALL	TYPE
	CALL	ILPRT
DB	CR,LF,CR,LF,'Command: '
DB	0

GETCMD:	LXI	D,CMDBUF ;ENTER COMMAND
	CALL	INBUFF
	CALL	CRLF
	LXI	D,CMDBUF+2 ;POINT TO COMMAND
	CALL	ILCOMP
DB	'CPM',0
	JNC	EXIT
	CALL	ILCOMP
DB	'DIR',0
	JNC	DIR
	CALL	ILCOMP
DB	'RET',0
	JC	NXTOPT1	;CARRY SET = NO MATCH
	lda	origsav	;@MODEM75.FIX
	sta	origflg	;@MODEM75.FIX
	LHLD	HLSAVE	;RETURN TO TERMINAL..
	JMP	TERM	;..MODE WITH SAVE OPTION..
			;..IF PREVIOUSLY ENABLED.
NXTOPT1:
	LDA	PMMIBYTE
	ORA	A
	JZ	S6
	CALL	ILCOMP	;DE SET FROM 1ST ILCOMP CALL
DB	'DSC',0
	JNC	DISCON1
	CALL	ILCOMP
DB	'BYE',0		;@7.63
	JNC	BYEBYE
S6:	CALL	ILCOMP
DB	'WRT',0
	JNC	WRTFIL
	CALL	ILCOMP
DB	'XPR',0
	JNC	XPRMODE
	CALL	ILCOMP
DB	'DEL',0
	JNC	NEWFILE
	LDA	PMMIBYTE
	ORA	A
	JZ	NXTOPT2
	CALL	ILCOMP
DB	'CAL',0
	JC	NXTOPT2
	MVI	A,20H		;FOOL THE SYSTEM'''
	STA	CMDBUF+4	;..CMDBUF SO THAT IT..
	JMP	DOOPT		;..LOOKS AT OPTION FOR DIAL

NXTOPT2:
	PUSH	H
	LDA	CMDBUF+2
	LXI	H,COMPLIST
	CALL	COMPARE	;COMPARES LIST POINTED TO BY HL..
	POP	H	;..TO CHAR IN A-REG.
	JC	MENU1	;CARRY SET = NO MATCH

DOOPT:	PUSH	H	;LOAD ORIGINAL FCB WITH TRANSFER..
	CALL	SETFCB	;..CMDS AND GO TO BEGINNING OF..
	POP	H	;..PROGRAM. WILL FOLLOW SAME LOGIC..
	JMP	RESTART	;..AS IF PROGRAM WERE CALLED WITH..
			;..CP/M COMMAND LINE.

DISCON1:
	LDA	PMMIBYTE
	ORA	A
	JZ	MENU
	CALL	DISCONNT
	CALL	ILPRT
DB	CR,LF,'++Disconnected++',CR,LF,BELL,0
	JMP	MENU

BYEBYE:	LDA	PMMIBYTE	;@7.63
	ORA	A
	JZ	MENU
	CALL	ILPRT
DB	CR,LF,'Goodbye, happy up/down loading',CR,LF,0
	XRA	A
	OUT	MODCTLP
	OUT	MODCTL2
	LHLD	CLDBOOT	;GET COLD BOOT PROM ADDRESS ;@7.63
	PCHL		;JUMP TO IT

DIR:	CALL	DIRLST
	JMP	XPRT

NEWFILE:
	LDA	FCB3+1
	CPI	' '
	JZ	MENU1	;IF NO FILE, DON'T ERASE
	LXI	D,FCB3
	MVI	C,ERASE
	CALL	BDOSRT
	MVI	A,TRUE	;DO NOT ALLOW TERMINAL..
	STA	NFILFLG	;..SAVE SINCE NO FILE..
	CMA		;..SPECIFIED.
	STA	SAVEFLG
	OUT	FRONTPAN
	LXI	H,FCB3
	CALL	INITFCBS
	JMP	MENU1

WRTFIL:
	LDA	NFILFLG
	CPI	TRUE
	JZ	MENU1
	LDA	FCB3+1	;CHECK THAT FILE WAS REQUESTED
	CPI	' '
	JZ	MENU1
	LHLD	HLSAVE
	CALL	NUMRECS	;DISK WRITE ROUTINE AS USED IN..
	CALL	WRTDSK	;..IN THE INTDSKSV ROUTINE.
	CALL	CLOSE3
	MVI	A,TRUE
	STA	NFILFLG
	CMA
	STA	SAVEFLG
	OUT	FRONTPAN
	LXI	H,FCB3
	CALL	INITFCBS ;BLANK OUT FCB SO WRITTEN FILE..
	JMP	MENU1	 ;..CAN'T BE ERASED.

XPRMODE:
	LDA	XPRFLG
	CMA
	STA	XPRFLG
	JMP	MENU1


COMPARE:
	MOV	B,M	;COMPARES A-REG WITH LIST..
COMPLP:	INX	H	;..ADDRESSED BY HL. FIRST ELEMENT..
	CMP	M	;..OF LIST MUST BE NUMBER OF ELEMENTS..
	JZ	VALID	;..BEING COMPARED. RETURNS WITH..
	DCR	B	;..CARRY SET IF A-REG DOES NOT..
	JNZ	COMPLP	;.. CONTAIN AN ELEMENT IN LIST.
	STC
VALID:	RET

COMPLIST: DB 5, 'S', 'R', 'T', 'E', 'M'

ILCOMP:	INLNCOMP	;A MACRO IN MACROS.LIB


INBUFF:	INBUF	;A MACRO IN "MACROS.LIB"

;IF ABOVE ROUTINE DOES NOT LET YOU EDIT IN A PROPER MANNER,
;THEN THE MACRO MAY BE SUBSTITUTED FOR THE FOLLOWING ROUTINE:

;INBUFF:
;	MVI	C,RDBUF
;	CALL	BDOSRT
;	RET	;BUT BE CAREFUL OF CONTROL-C


CPMLINE:
	CMDLINE	;A MACRO IN "MACROS.LIB"

DIRLST:	DIRLIST	;A MACRO IN "MACROS.LIB"

NFILFLG:
	DB FALSE	;NORMALLY SET TO FALSE. ALLOWS WRITE TO..
		;..MEMORY IN TERMINAL MODE.

OPTION:	DB 0

OPTBL:	EQU $
ANSWFLG:
	DB 'A'
DISCFLG:
	DB 'D'
ORIGFLG:
	DB 'O'
QFLG:	DB 'Q'
RSEEFLG:
	DB 'R'
SSEEFLG:
	DB 'S'
VSEEFLG:
	DB 'V'
TERMFLG:
	DB 'T'
EPARITY:
	DB '0'	;EVEN PARITY SUB-OPTION - ONLY AVAILABLE IN 'S' AND 'R' MODES
OPARITY:
	DB '1'	;ODD PARITY SUB-OPTION - ONLY AVAILABLE IN 'S' AND 'R' MODES
BATCHFLG:
	DS 1	;SET TO 'B' BY MENU. DOES NOT ALLOW MULTI-..
OPTBE:	EQU $	;..FILE XFER WHEN PROGRAM INITIALLY CALLED.

RESTROPT:	;MUST BE IN SAME ORDER AS TABLE ABOVE

	DB 'A','D','O','Q','R','S','V','T'
	DB '0','1','B'

CRCFLG:
	DB 'C'	;use CRC instead of cksum	;@7.62 (moved)

RESTSN:	DB 0,0,0,0,0,0
	DW DBUF
	DB 0,0,0,0,0

SECTNOB:	EQU $
RCVSNO:		DB 0
SECTNO:		DW 0
ERRCT:		DB 0
ERRCDE:		DB 0
EOFLG:		DB 0
SECPTR:		DW DBUF
SECINBF:	DB 0
MAXEXT:		DB 0
RCNT:		DW 0
DATAFLG:	DB 0
EXACFL:		DB 0
SECTNOE:	EQU $

BADOPT:	CALL	ILPRT
DB	'Invalid option',CR,LF,BELL,0
	JMP	MENU

FSTFLG:	DB TRUE
FIRSTME:
	DB	0FFH
		;first SOH received switch(it is zero after 1rst SOH)

CMDBUF:	DB	80H,0
	DS	80H
BADLIB:	DB	CR,LF,'++Bad library number called++',CR,LF,'$'
HLSAVE:	DS	2
DISKNO:	DS	1
SENDFLG:
	DS	1
NBSAVE:	DS	2
BGNMS:	DS	2
FILECT:	DS	1
NAMECT:	DS	1
origsav: ds	1	;@MODEM75.FIX
MODCTLB:
	DB	07FH
UARTFLG:DB	0FFH	; @7.61
UARTCTLB:
	DB	ANSWMOD	; @7.5  (was previously ORIGMOD)

	DS	100
STACK:	DS	2
FCB3:	DS	33
FCBBUF:	DS	15
DBUF:		EQU	$	
NAMEBUF:	EQU	DBUF+(DBUFSIZ*1024)
		;BUFFER FOR NAMES IN BATCH MODE. OVERFLOWS..
		;..ABOVE PROGRAM CODE.

;	BDOS EQUATES
RDCON		EQU	1
WRCON		EQU	2
PRINT		EQU	9
RDBUF		EQU	10
CONST		EQU	11
OPEN		EQU	15
CLOSE		EQU	16
SRCHF		EQU	17
SRCHN		EQU	18
ERASE		EQU	19
READ		EQU	20
WRITE		EQU	21
MAKE		EQU	22
REN		EQU	23
STDMA		EQU	26
FILSIZ		EQU	35
BDOS		EQU	5
REIPL		EQU	0
FCB		EQU	5CH
FCBEXT		EQU	FCB+12
FCBSNO		EQU	FCB+32
FCBRNO		EQU	FCB+32
FCB2		EQU	6CH

LAST		END	100H

