;			PLINK.ASM
;          (latest version JAN 24 1982 by BC Reagan)
;		 (prior version OCT 18 1980)
;
;     PLINK - SUPPORT COMMUNICATIONS LINK WITH CYBER
;
;PLINK IS A CP/M TRANSIENT COMMAND WHICH ALLOWS THE USER TO
;ESTABLISH A COMMUNICATIONS LINK WITH A REMOTE COMPUTER
;
;	ORIGINAL BY L.E. HUGHES    EDCAM	JULY, 1977
;
;	   This version by Keith Petersen, W8SDZ.
;	   WITH HEATH EQUATES ADDED BY TOM JORGENSON
;
; TRS-80 MODEL 1 mods by Steve Vinokuroff, Vanc CBBS
; Optional Triger characters by Steve Vinokuroff
; TRS-80 mods by Dennis Breckenridge, Burnaby CBBS
; D.C.Hayes mods by Bruce Ratoff, Iselin NJ Remote CP/M
; CP/M Alternate Console Device mods added by
;	Ben Reagan, Lynnwood, WA
;
;This program currently supports the following modems
; or computers via conditional assembly.
;
;	1. PMMI   modem
;	2. ANY serial i/o board (TUART INCLUDED)
;	3. TRS-80 model 1
;	4. TRS-80 model 2
;	5. HEATH H8 WITH 8251 UART AT PORT 330Q
;	6. D.C.Hayes 80-103A or Micromodem 100
;	7. External dumb modem connected to a physical CP/M
;	   device (TTY, CRT, BAT, or UC1 but different than
;          local console)
;       8. OSBORNE 1
;
;-->NOTE: IF ASSEMBLED AS WRITTEN WILL WORK WITH AN external dumb
;         modem connected to the CP/M 'CRT' device port.
;
;PLINK CURRENTLY SUPPORTS TWO WAY TRANSFER OF TEXT FILES
;BETWEEN THE CP/M DISK AND THE REMOTE COMPUTER. THE FOLLOWING
;CONTROL CODES MAY BE INITIATED FROM THE CONSOLE KEYBOARD:
;
;	****************************************************
;	*		      COMMANDS:		   	   *
;	*						   *
;	*  CONTROL E	EXIT PLINK TO CP/M WARM BOOT	   *
;	*  CONTROL T	TRANSMIT ASCII FILE TO MODEM.      *
;	*		ASKS FOR DRIVE AND FILENAME.TYP    *
;	*  CONTROL C	ABORT FILE SEND TO MODEM	   *
;	*  CONTROL Y	SAVE INCOMING ASCII IN RAM BUFFER  *
;	*		FOR LATER TRANSFER TO DISK	   *
;	*  CONTROL Q	WRITE RAM BUFFER TO DISK - ASKS    *
;	*		FOR DRIVE AND FILENAME.TYP	   *
;	*  DELETE	BACKSPACE WHEN IN COMMAND MODE	   *
;	*		ASKING FOR FILENAME		   *
Š;	*  CONTROL U	ABORT CURRENT LINE WHEN IN COMMAND *
;	*		MODE ASKING FOR FILENAME	   *
;	*						   *
;	*  (NOTE: ALL OTHER CONTROL CODES ARE PASSED TO    *
;	*	  MODEM OUTPUT)				   *
;	****************************************************
;
;
;CONDITIONAL ASSEMBLY SWITCHES <<-- SET FOR YOUR SYSTEM
;
TUART	SET	0	;CROMEMCO TUART I/O BOARD
PMMI	EQU	0	;PUT A 1 HERE IF YOU HAVE A PMMI
DCH	EQU	0	;PUT A 1 HERE IF YOU HAVE A D.C.HAYES
TRS1	EQU	0	;PUT A 1 HERE IF YOU HAVE TRS80-MOD1
TRSPT	EQU	0	;PUT 1 HERE IF YOU HAVE TRS80-MOD2
			;USING PICKLES & TROUT CP/M 2.X
H84	EQU	0	;PUT 1 HERE IF YOU HAVE H8/H8-4
CPMDUM	EQU	1	;PUT 1 HERE IF YOU ARE USING A CP/M
			;CONSOLE I/OBYTE DEVICE WITH AN EXTERNAL
			;DUMB (ACOUSTIC) MODEM.  BE SURE TO
			;SET THE ALTERNATE CONSOLE DEVICE BELOW.
	IF	CPMDUM
ALTCON	EQU	1	;PUT ALTERNATE (MODEM) CONSOLE
			;DEVICE HERE. 0=TTY 1=CRT 2=BAT 3=UC1
			;IMPORTANT: THE 'ALTCON' MUST BE
			;DIFFERENT THAN THE NORMAL CONSOLE
			;DEVICE USED BY THE LOCAL OPERATOR,
			;SINCE THIS PROGRAM SWITCHES BETWEEN
			;THE TWO DEVICES.
ENDIF
	IF	H84
TUART	SET	1
	ENDIF
;
OSBORNE EQU     1       ;PUT 1 HERE IF OSBORNE 1
;
INIT$REQUIRED	EQU	1 ;PUT 1 HERE IF INITIALIZATION NEEDED
			  ;(NEEDED FOR MOST VERSIONS INCLUDING
			  ; 'CPMDUM')
;
	IF	TRS1 OR PMMI OR TUART OR DCH
PORT	EQU	1
	ENDIF
;
	IF	TRSPT OR CPMDUM
PORT	EQU	0	;STILL NEED THE SWITCH
	ENDIF
;
;
;
;BDOS ENTRY POINT AND FUNCTION CODES
;
	IF	NOT	TRS1
BASE	SET	0	;STANDARD CPM
	ENDIF
;
	IF	TRS1 OR H84
ŠBASE	SET	4200H	;TRS-80 MODEL 1 CP/M BASE ADDRESS
	ENDIF
;
BDOS	EQU	BASE+5
RESDSK	EQU	13	;RESET DISK SYSTEM
OFFC	EQU	15	;OPEN FILE
CFFC	EQU	16	;CLOSE FILE 
DFFC	EQU	19	;DELETE FILE
RRFC	EQU	20	;READ RECORD
WRFC	EQU	21	;WRITE RECORD 
MFFC	EQU	22	;MAKE FILE
IOGET	EQU	7	;GET I/O STEERING BYTE
IOSET	EQU	8	;SET I/O STEERING BYTE
;
;	TRS80 PICKLES AND TROUT SIO CALLS
;	OFFSET BY -3 THAT IS ADD 3 TO ALL CALLS
;
SETSIO	EQU	30H	;SET UP Z80 SIO
SIOTST	EQU	33H	;READ SIO STATUS
SIOINP	EQU	36H	;INPUT A CHAR
SIOOUT	EQU	39H	;OUTPUT A CHAR
;
;DEFAULT FCB AND FIELD DEFINITIONS 
;
FCB	EQU	BASE+5CH
FN	EQU	1	;FILE NAME FIELD (REL)
FT	EQU	9	;FILE TYPE FIELD (REL)
EX	EQU	12	;FILE EXTENT FIELD (REL)
NR	EQU	32	;NEXT RECORD FIELD (REL)
DBUF	EQU	BASE+80H ;DEFAULT DISK BUFFER ADDRESS
;
;ASCII CONTROL CHARACTERS
;
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
DEL	EQU	7FH	;DELETE (RUBOUT)
BELL	EQU	07H	;BELL SIGNAL
TAB	EQU	09H	;HORIZONTAL TAB
XON	EQU	11H	;X-ON CHARACTER
NULL	EQU	00H	;NULL CHAR
;
;THE FOLLOWING "TRIGER" EQUATE IS SET TO "LF" (LINEFEED)
;BY DEFAULT. AN OPTIONAL TRIGER CHAR MAY BE PASSED VIA FCB1
;
; IE:  PLINK B		WILL SET TRIGER TO "BELL"
;
;THE FOLLOWING OPTIONS ARE ALLOWED
;
;	1. B = BELL  07H
;	2. X = XON   11H
;	3. U = UPLOAD NO TRIGER CHECK AT ALL
;ANY OTHER ASCII CHARACTER MAY BE PASSED THROUGH FCB1
;
;
TRIGER	EQU	LF	;DEFAULT VALUE
Š;
;
;WARNING CHARACTER FOR LOW MEMORY
;
WRNSIG	EQU	BELL	;IF YOU HAVE ONE, PUT 'BELL' HERE
			;...ELSE PUT '*' HERE.
;
;MODEM I/O PORT ADDRESSES
;
	IF	PMMI
MODD	EQU	0C1H	;MODEM DATA PORT
MODS	EQU	0C0H	;MODEM STATUS PORT
MODINIT	EQU	29H	;INITIALIZE BYTE ORIGINATE,
			;7 DATA, EVEN PARITY, 1 STOP
	ENDIF
;
	IF	DCH
MODD	EQU	80H	;MODEM DATA PORT
MODS	EQU	81H	;MODEM STATUS PORT
MODINIT	EQU	05H	;7 DATA, EVEN PARITY, 1 STOP
	ENDIF
;
	IF	TRS1
MODD	EQU	0EBH	;TRS80 MOD 1 RS232 DATA PORT
MODS	EQU	0EAH	; AND THE RS232 STATUS PORT
	ENDIF
;
	IF	TUART
MODD	EQU	0D8H	;<<--MODIFY FOR YOURS
MODS	EQU	0DDH	;<<--MODIFY FOR YOURS
	ENDIF
;
;MODEM STATUS PORT BIT DEFINITIONS
;
	IF	PMMI
MTBE	EQU	01H	;MODEM TRANS. BUFFER READY FLAG
MRDA	EQU	02H	;MODEM RECEIVE DATA AVAIL. FLAG
MXOR	EQU	03H	;MASK TO MAKE MTBE AND MRDA "LOW TRUE"
	ENDIF
;
	IF	DCH
MTBE	EQU	02H	;MODEM TRANS. BUFFER READY FLAG
MRDA	EQU	01H	;MODEM RECEIVE DATA AVAIL. FLAG
MXOR	EQU	03H
	ENDIF
;
	IF	TRS1
MTBE	EQU	40H	;TRS80 MOD1 RS232 BUFFER READY
MRDA	EQU	80H	;MODEM RECEIVE DATA AVAIL.
MXOR	EQU	0C0H
	ENDIF
;
	IF	TUART	;<<--OR ANY OTHER SERIAL I/O
MTBE	EQU	20H	;<<--MODIFY FOR YOURS
MRDA	EQU	1H	;<<--MODIFY FOR YOURS
ŠMXOR	EQU	21H	;<<--MODIFY FOR YOURS
	ENDIF
;
;	**MAIN PROGRAM**
;
	ORG	BASE+100H
LINK:   IF      NOT OSBORNE
        LXI	SP,STACK+64	;CREATE LOCAL STACK 
        ENDIF
	LHLD	BASE+1	;POINT TO CP/M JMP TABLE
	LXI	D,3	;GET READY TO ADD 3
	DAD	D	;POINT TO CON STATUS JMP
	SHLD	CITCAL+1 ;MODIFY CALL ADRS
	DAD	D	;POINT TO CON IN JMP
	SHLD	RCCAL+1	;MODIFY CALL ADRS
	DAD	D	;POINT TO CON OUT JMP
	SHLD	WCCAL+1	;MODIFY CALL ADRS
	LDA	FCB+1	;SEE IF OPTIONAL TRIGER CHAR
	CPI	20H	;BLANK.. ?
	JZ	SKP	;..BLANK SO USE DEFAULT "LF"
	CPI	'B'	;BELL WANTED
	JZ	TRGBEL
	CPI	'X'	;XON WANTED
	JZ	TRGXON
	CPI	'U'	;UPLOADING NO CHECKING FOR TRIGER
	JZ	TRGUPL
;
SETTRG	STA	OVERLY+1 ;STORE THE CHARACTER AS IS THEN
	JMP	SKP
;
TRGBEL	MVI	A,BELL
	JMP	SETTRG
;
TRGXON	MVI	A,XON
	JMP	SETTRG
;
TRGUPL	XRA	A		;ZERO OUT JUMP
	STA	OVERL1+1	;CHANGE CHECK FOR C/R TO NULL
	STA	OVERL2+1	;AND SEND LINEFEEDS AS WELL
	JMP	SKP
;
SKP	EQU	$
	IF	H84
	MVI A,80H;	SET DLAB BIT IN 8250 UART
	OUT 0DBH;	8250 AT PORT D8H (330Q)
	NOP ! NOP ! NOP
	NOP ! NOP
	MVI A,01H;	MSB OF BAUD RATE DIVISOR
	OUT 0D9H;	...TO UART
	NOP ! NOP ! NOP
	NOP ! NOP
	MVI A,80H;	LSB OF BAUD RATE DIVISOR
	OUT 0D8H;	...TO UART
	NOP ! NOP ! NOP
	NOP ! NOP
	MVI A,03H;	8 BITS, 1 STOP BIT, NO PARITY, DLAB RESET
Š	OUT 0DBH;	...TO UART
	NOP ! NOP ! NOP
	NOP ! NOP
	MVI A,0;	RESET CONTROL REGISTER
	OUT 0DCH;	...TO UART
	JMP	CONT
	ENDIF
	IF	INIT$REQUIRED AND NOT H84 AND NOT CPMDUM OR PMMI AND NOT H84
	MVI	A,MODINIT
	OUT	MODS	;INITIALIZE MODEM PORT
	ENDIF
;
	IF	TUART
	MVI	A,80H	;DSR ON BIT 7 PARL PORT B
	OUT	54H
	ENDIF
;
	IF	TRSPT		;MUST SET UP SERIAL CHANNEL
RESET:	LXI	H,INITR		;STORE RETURN ADDRESS
	PUSH	H
	LHLD	1
	LXI	D,SETSIO	;SIO SETUP ROUTINE
	DAD	D
	PUSH	H		;STORE ON STACK
	MVI	C,00H		;NO PARITY CHAN-A
	MVI	D,0E6H		;8 bits ,1 STOP
	MVI	E,3		;300 BAUD
	MVI	L,00H		;DISABLE EXT/ACK SIO FUNCTIONS
	MVI	H,'S'-40H	;CONTROL S (X-ON)
	RET			;TROUGH SETUP PROG
INITR	NOP			;DO IT TO IT
	ENDIF
;
	IF	TRS1		;INIT FOR TRS80 MOD1 RS232
	OUT	0E8H		;RESET RS232
	IN	0E9H		;READ THE SWITCHES
	ANI	0F8H
	ORI	5
	OUT	0EAH		;SET DSR AND CTS
	MVI	A,55H		;300 BAUD
	OUT	0E9H
	ENDIF
;
;
	IF	PORT
	IN	MODD         ;CLEAR MODEM UART READ BUFFERS
	IN	MODD 
	ENDIF
;
	IF	CPMDUM
SAVIO:	PUSH H ! PUSH D ! PUSH B ! PUSH PSW
	MVI	C,IOGET	;SAVE ENTRY I/OBYTE
	CALL	BDOS
	STA	ORGIO
	POP PSW ! POP B ! POP D ! POP H
Š	ENDIF
;
CONT	XRA	A	;CLEAR CHAR BUFFERS 
	STA	INCH 
	STA	OUTCH
	STA	FLAG	;CLEAR TEXT SAVE FLAG 
	LXI	H,TBUF	;SET PTR TO TBUF
	SHLD	PTR
	LXI	H,0	;SIZE = 0 
	SHLD	SIZE 
	LXI	H,LINKMS  ;PRINT SIGN-ON MESSAGE
	CALL	WCS
;
;	 MAIN LOOP 
;
LINK3:	CALL	CITEST	;JUMP IF NO DATA FROM CONSOLE 
	JZ	LINK4
	CALL	RCC	;ELSE READ CONSOLE DATA
	CPI	20H
	CC	PCC	;CALL PCC IF CONTROL CHAR
	JC	LINK4	;JUMP IF PCC HANDLED CHAR 
	ORI	80H	;ELSE SET VALID DATA BIT
	STA	INCH	;AND STORE IN INPUT CHAR BUFFER 
LINK4:	LDA	OUTCH	;JUMP IF NO DATA FOR CONSOLE
	ORA	A
	JP	LINK5
	ANI	7FH	;ELSE DISCARD VALID DATA BIT
	CALL	WCC	;SEND CHAR TO CONSOLE 
	XRA	A	;THEN CLEAR OUTPUT CHAR BUFFER
	STA	OUTCH
LINK5:	CALL	MITEST	;JUMP IF NO DATA FROM MODEM 
	JZ	LINK6
	CALL	RMC2	;ELSE READ MODEM DATA
	CALL	SAVE	;SAVE CHAR IN TEXT BUFFER IF FLAG ON
	ORI	80H	;SET DATA VALID BIT 
	STA	OUTCH	;STORE IN OUTPUT CHAR BUFFER
LINK6:	CALL	MOTEST	;JUMP IF MODEM XMIT BUFFER BUSY 
	JZ	LINK7
	LDA	INCH	;JUMP IF NO DATA FOR MODEM
	ORA	A
	JP	LINK7
	ANI	7FH	;DISCARD VALID DATA BIT
;
	IF	PORT
	OUT	MODD	;OUTPUT CHAR TO MODEM 
	ENDIF
;
	IF	TRSPT
	PUSH	B	;STORE REGISTERS
	PUSH	H
	PUSH	D
	CALL	WMC	;SEND CHAR
	POP	D
	POP	H
	POP	B
Š	ENDIF
;
	IF	CPMDUM
	CALL	WMC	;SEND CHAR TO MODEM
	ENDIF
;
	XRA	A	;...THEN CLEAR INPUT CHAR BUFFER 
	STA	INCH 
LINK7:	JMP	LINK3	;END OF MAIN LOOP 
;
LINKMS:	DB	CR,LF,'PLINK as of 24-JAN-82'
	IF	TUART
	DB	CR,LF,'(For TUART I/O)'
	ENDIF
	IF	PMMI
	DB	CR,LF,'(For PMMI Modem)'
	ENDIF
	IF	DCH
	DB	CR,LF,'(For DCH Modem)'
	ENDIF
	IF	TRS1
	DB	CR,LF,'(For TRS80-MOD1)'
	ENDIF
	IF	TRSPT
	DB	CR,LF,'(For TRS80-MOD2)'
	ENDIF
	IF	H84
	DB	CR,LF,'(For H8/H8-4)'
	ENDIF
	IF	CPMDUM
	DB	CR,LF,'(For CP/M Alternate Console Device'
	DB	CR,LF,'with External Dumb Modem)'
	ENDIF
	DB	CR,LF,LF,'READY',CR,LF,LF,0
;
;	 PCC - PROCESS CONTROL CHARACTER 
;
PCC:	CPI	'E'-40H	;JUMP OUT IF CTRL E
	JNZ	PCC1 
	PUSH	H
	LXI	H,AYS	;PRINT 'ARE YOU SURE'
	CALL	WCS
	POP	H
	CALL	RCC	;GET ANSWER
	CALL	WCC	;ECHO IT
	ANI	5FH	;MAKE UPPER CASE
	CPI	'Y'	;YES?
	JZ	PCCEX	;EXIT
	CALL	WCCR	;CRLF
	STC		;TELL LINK TO IGNORE THIS CHARACTER 
;
	IF	TRSPT
	POP	PSW	;GOBBLE UP CALL ADDRESS
	JMP	RESET	;RE-INITIALIZE SIO
	ENDIF
Š;
	IF	PORT OR CPMDUM
	RET
	ENDIF
;
PCC1:	CPI	'T'-40H	;JUMP IF NOT CONTROL-T
	JNZ	PCC2 
	CALL	STF	;TRANSMIT TEXT FILE TO MODEM
	STC		;TELL LINK TO IGNORE THIS CHARACTER 
	RET 
;
PCC2:	CPI	'Y'-40H	;JUMP IF NOT CONTROL-Y
	JNZ	PCC3 
	MVI	A,1	;TURN ON TEXT SAVE FLAG 
	STA	FLAG 
	LXI	H,PCCMR	;PRINT 'SAVING INCOMING TEXT IN MEMORY'
	CALL	WCS
	STC		;TELL LINK TO IGNORE THIS CHARACTER
	RET 
;
PCC3:	CPI	'Q'-40H	;JUMP IF NOT CONTROL-Q
	JNZ	PCC4 
	XRA	A	;TURN OFF TEXT SAVE FLAG
	STA	FLAG 
	CALL	WTB	;WRITE TEXT BUFFER TO DISK
	STC 
	RET 
;
PCC4:	STC		;LET LINK HANDLE ALL OTHER CONT. CODES
	CMC 
	RET 
;
PCCEX:	LXI	H,DISMS	;PRINT 'MODEM NOT DISCONNECTED'
	CALL	WCS
	JMP	BASE	;EXIT TO WARM BOOT
;
AYS:	DB	CR,LF,'EXIT TO CP/M - ARE YOU SURE (Y OR N)?',0
	IF	PMMI OR DCH
DISMS:	DB	CR,LF,'++DON''T FORGET - THE MODEM '
	DB	'IS NOT DISCONNECTED++',CR,LF
	DB	'USE "MODEM D" TO DISCONNECT',0
	ENDIF
;
	IF	NOT PMMI AND NOT DCH
DISMS:	DB	CR,LF,'+++ EXIT TO CP/M +++',CR,LF,0
	ENDIF
;
PCCMR:	DB	CR,LF,'SAVING INCOMING TEXT IN MEMORY',CR,LF,0
;
;	 STF - SEND TEXT FILE (TO MODEM) 
;
STF:	CALL	GFN	;GET NAME OF DISK FILE TO SEND
	JC	STF6	;JUMP IF FILE NAME ERROR
	CALL	OPEN	;TRY TO OPEN SPECIFIED FILE 
	CPI	255	;JUMP IF FILE NOT FOUND 
Š	JZ	STF7 
STF1:	CALL	READ	;READ NEXT RECORD INTO DBUF 
	CPI	1	;JUMP IF END-OF-FILE
	JZ	STF5 
	LXI	H,DBUF 	;POINT TO DISK BUFFER 
	MVI	C,128
STF2:	MOV	A,M	;FETCH NEXT CHAR FROM DBUF
	INX	H
	CPI	'Z'-40H	;JUMP IF END-OF-FILE CHARACTER
	JZ	STF5 
OVERL2	CPI	LF	;IGNORE LINE FEEDS
	JZ	STF4 
	CALL	WMC	;WRITE CHARACTER TO MODEM 
	CALL	WCC	;WRITE CHARACTER TO CONSOLE 
OVERL1	CPI	CR	;JUMP IF NOT CARRIAGE RETURN
	JNZ	STF4 
STF3:	CALL	CITEST	;CHECK CONSOLE DATA READY
	JZ	STF3A	;NO DATA THERE
	CALL	RCC	;GET CONSOLE CHARACTER
	CPI	'C'-40H	;CONTROL C ABORTS IT
	JZ	STF8
STF3A:	CALL	MITEST	;WAIT FOR NEXT MODEM KHARACTER
	JZ	STF3
	CALL	RMC2	;CHECK MODEM FOR TRIGGER CHAR.
OVERLY	CPI	TRIGER
	JNZ	STF3 
	CALL	WCCR	;SEND CRLF TO CONSOLE
STF4:	DCR	C	;LOOP THRU REST OF DBUF 
	JNZ	STF2 
	JMP	STF1	;GO GET NEXT RECORD FROM DISK 
;
STF5:	LXI	H,STFSM	;PRINT 'FILE SEND COMPLETE'
	CALL	WCS
	RET 
;
STF6:	LXI	H,STFS1	;PRINT 'FILE NAME ERROR'
	CALL	WCS
	RET 
;
STF7:	LXI	H,STFS2	;PRINT 'FILE NOT FOUND' 
	CALL	WCS
	RET 
;
STF8:	LXI	H,STFSA	;PRINT 'FILE SEND ABORTED'
	CALL	WCS
	RET
;
STFSM:	DB	'FILE SEND COMPLETE',CR,LF,0
STFS1:	DB	'FILE NAME ERROR',CR,LF,0
STFS2:	DB	'FILE NOT FOUND',CR,LF,0 
STFSA:	DB	CR,LF,'FILE SEND ABORTED',CR,LF,0
;
;	 SAVE - SAVE CHAR IN TEXT BUFFER IF FLAG ON
; 
;	 ENTRY CONDITIONS
Š;		A - CHARACTER TO SAVE
;
SAVE:	PUSH	PSW
	LDA	FLAG 
	ORA	A
	JNZ	SAVE1
	POP	PSW
	RET 
;
SAVE1:	POP	PSW
	CPI	DEL	;RUBOUT (DEL) ?
	RZ		;YES, IGNORE IT
	CPI	20H	;TEST FOR CONTROL CHARACTERS
	JNC	SAVE2	;JUMP IF NOT CONTROL CHAR.
	CPI	CR	;ALLOW CR TO BE SAVED
	JZ	SAVE2
	CPI	LF	;ALLOW LF TO BE SAVED
	JZ	SAVE2
	CPI	TAB	;ALLOW TAB TO BE SAVED
	JZ	SAVE2
	RET		;IGNORE ALL OTHER CONTROL CHARS.
;
SAVE2:	PUSH	H
	LHLD	SIZE	;SIZE = SIZE + 1
	INX	H
	SHLD	SIZE 
	LHLD	PTR
	MOV	M,A
	INX	H
	SHLD	PTR
	PUSH	PSW
	LDA	BASE+7	;GET SYSTEM SIZE
	SUI	1	;SO WE DONT CRASH CP/M
	CMP	H	;ARE WE OUT OF ROOM?
	JZ	SAVEAB	;YES, ABORT
	SUI	4	;LEAVE SOME ROOM (1K)
	CMP	H
	MVI	A,WRNSIG  ;SIGNAL CONSOLE RUNNING OUT OF SPACE
	CC	WCC
	POP	PSW
	POP	H
	RET 
;
;	SAVEAB - RAN OUT OF ROOM, ISSUE MESSAGE AND FLOW
;		 THROUGH TO DISK SAVE ROUTINE
;
SAVEND:	DB	BELL,CR,LF,'ABORTING - NO ROOM LEFT',0
;
SAVEAB:	LXI	SP,STACK+64  ;REINITIALIZE STACK
	LXI	H,SAVEND  ;PRINT 'ABORTING - NO ROOM LEFT'
	CALL	WCS
	LXI	H,LINK	;SET UP RETURN ADDRESS
	PUSH	H	;LEAVE IT ON THE STACK
;
;	 WTB - WRITE TEXT BUFFER TO DISK 
Š;
WTB:	LHLD	SIZE	;JUMP IF TEXT BUFFER EMPTY
	MOV	A,L
	ORA	H
	JZ	WTB5 
	MVI	C,RESDSK ;RESET IN CASE READ-ONLY
	CALL	BDOS
	CALL	GFN	;GET FILE NAME
	JC	WTB6	;JUMP IF FILE NAME ERROR
	CALL	DELT	;DELETE OLD FILE, IF ANY
	CALL	MAKE	;MAKE NEW FILE
	LHLD	SIZE	;DE = TBUF SIZE 
	XCHG
	LXI	H,DBUF	;TOP OF STACK POINTS TO DBUF
	PUSH	H
	LXI	H,TBUF	;HL POINTS TO TBUF
WTB1:	MVI	C,128	;DISK BUFFER SIZE 
WTB2:	MOV	A,M	;FETCH NEXT BYTE OF TBUF
	INX	H
	XTHL
	MOV	M,A	;STORE IN DBUF
	INX	H
	XTHL
	DCX	D	;SIZE = SIZE - 1
	MOV	A,D	;EXIT LOOP IF SIZE = 0
	ORA	E
	JZ	WTB3 
	DCR	C	;LOOP UNTIL DBUF FULL 
	JNZ	WTB2 
	CALL	WRITE	;WRITE FULL DBUF TO DISK
	XTHL		;TOP OF STACK POINTS TO DBUF
	LXI	H,DBUF 
	XTHL
	JMP	WTB1	;LOOP UNTIL END OF TBUF 
;
WTB3:	POP	H	;HL POINTS TO CURRENT PLACE IN DBUF 
WTB4:	MVI	M,'Z'-40H ;STORE EOF CODE 
	INX	H
	DCR	C	;LOOP THRU REST OF DBUF 
	JNZ	WTB4 
	CALL	WRITE	;WRITE LAST SECTOR TO DISK
	CALL	CLOSE	;CLEAN UP ACT AND GO HOME 
	LXI	H,TBUF	;CLEAR TEXT BUFFER
	SHLD	PTR
	LXI	H,0
	SHLD	SIZE 
	LXI	H,WTBSM	;PRINT 'BUFFER SAVED ON DISK'
	CALL	WCS
	RET 
;
WTB5:	LXI	H,WTBS1	;PRINT 'TEXT BUFFER EMPTY'
	CALL	WCS
	RET 
;
WTB6:	LXI	H,WTBS2	;PRINT 'FILE NAME ERROR'
Š	CALL	WCS
	RET 
;
WTBSM:	DB	CR,LF,'BUFFER SAVED ON DISK',CR,LF
	DB	'MEMORY SAVE CANCELLED',CR,LF,0
WTBS1:	DB	'TEXT BUFFER EMPTY',CR,LF,0
WTBS2:	DB	'FILE NAME ERROR',CR,LF,0
;
;	 WCS - WRITE CONSOLE STRING
;
; 
;	 ENTRY CONDITIONS
;		HL - POINTS TO STRING (TERM BY ZERO BYTE)
;
WCS:	MOV	A,M
	INX	H
	ORA	A
	RZ
	CALL	WCC
	JMP	WCS
;
;	 WCCR - WRITE CONSOLE CARRIAGE RETURN (AND LINE FEED)
;
WCCR:	MVI	A,CR 
	CALL	WCC
	MVI	A,LF 
;
;	 WCC - WRITE CONSOLE CHARACTER 
; 
;	 ENTRY CONDITIONS: 
;		A - CHARACTER TO WRITE 
;
WCC:	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A	;GET CHARACTER FOR CBIOS
WCCAL:	CALL	$-$	;MODIFIED BY INIT.
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET 
;
;	 RCS - READ CONSOLE STRING (WITH ECHO) 
; 
;	 EXIT CONDITIONS 
;		B - NUMBER OF CHARACTERS READ (<255) 
;		HL - POINTS TO LAST CHAR STORED (CR) 
;
RCS:	LXI	H,IBUF 
	MVI	B,0
RCS1:	CALL	RCC	;READ NEXT CHAR FROM CONSOLE
	CPI	DEL	;JUMP IF NOT DEL
	JNZ	RCS2 
Š	INR	B	;IGNORE DEL IF IBUF ALREADY EMPTY 
	DCR	B
	JZ	RCS1 
	DCX	H	;ELSE DISCARD LAST CHAR 
	MOV	A,M	;ECHO DISCARDED CHAR TO CONSOLE 
	CALL	WCC
	DCR	B	;DECREMENT COUNT
	JMP	RCS1	;	AND LOOP 
;
RCS2:	CPI	'U'-40H	;JUMP IF NOT CONTROL U
	JNZ	RCS3 
	CALL	WCCR	;ELSE ABORT CURRENT LINE
	JMP	RCS	;	AND START OVER 
;
RCS3:	CALL	WCC	;ECHO CHAR TO CONSOLE 
	MOV	M,A	;STORE CHAR IN IBUF 
	INR	B	;INCREMENT COUNT
	CPI	CR	;JUMP IF CARRIAGE RETURN
	JZ	RCS4
	INX	H	;ELSE ADVANCE POINTER 
	JMP	RCS1	;	AND LOOP 
;
RCS4:	MVI	A,LF	;ISSUE LINE FEED AND RETURN 
	CALL	WCC
	RET 
;
;	 RCC - READ CONSOLE CHARACTER
; 
;	 EXIT CONDITIONS 
;		A - CHARACTER READ 
;
RCC:	PUSH	B
	PUSH	D
	PUSH	H
RCCAL:	CALL	$-$	;MODIFIED BY INI\.
	POP	H
	POP	D
	POP	B
	RET 
;
;	 WMC - WRITE MODEM CHARACTER 
; 
;	 ENTRY CONDITIONS
;		A - CHARACTER TO WRITE 
;
;
	IF	PORT
WMC:	PUSH	PSW
WMCL:	IN	MODS 
	XRI	MXOR
	ANI	MTBE
	JNZ	WMCL
	POP	PSW
	ANI	7FH	;STRIP PARITY BIT
	OUT	MODD 
Š	RET 
	ENDIF
;
	IF	TRSPT
WMC:	PUSH	H
	PUSH	D
	PUSH	PSW
WMCL:	CALL	MOTEST	;TEST STATUS
	JZ	WMCL	;LOOP TILL TX EMPTY
	POP	PSW	;RESTORE CHAR
	ANI	7FH	;STRIP PARITY
	PUSH	B	;STORE B
	MOV	C,A	;PUT CHAR INTO C
	MVI	B,00H	;CHANNEL A
	LXI	H,WMCRE	;STORE RETURN ADDRESS
	PUSH	H
	LHLD	1	;GET BASE ADDRESS
	LXI	D,SIOOUT
	DAD	D
	PCHL		;JUMP TO IT
WMCRE:	POP	B	;RESTORE IT
	POP	D
	POP	H
	RET
	ENDIF
;
	IF	CPMDUM
WMC:	PUSH H ! PUSH D ! PUSH B
	PUSH PSW
WMCL:	LDA	ORGIO	;GET ENTRY I/OBYTE
	ANI	0FCH	;SAVE ALL BITS EXCEPT 0 & 1
	ADI	ALTCON	;SET BITS 0 & 1 TO 'CRT'
	MOV	E,A
	MVI	C,IOSET ;SET NEW I/OBYTE
	CALL	BDOS
	POP	PSW
	ANI	7FH
	CALL	WCC	;OUTPUT CHAR TO MODEM
	LDA	ORGIO ;RESTORE ENTRY I/OBYTE
	MOV	E,A
	MVI	C,IOSET ;SET I/OBYTE
	CALL	BDOS
	POP B ! POP D ! POP H
	RET
;
;	 RMC - READ MODEM CHARACTER
; 
;	 EXIT CONDITIONS:
;		A - CHARACTER READ 
;
;
	IF	PORT
RMC:	IN	MODS 
	XRI	MXOR
	ANI	MRDA
Š	JNZ	RMC
RMC2:	IN	MODD 
	ANI	7FH 
	RET 
	ENDIF
;
	IF	TRSPT
RMC:	CALL	MITEST	;CHAR AVAILABLE
	JZ	RMC	;LOOP IF NOT READY
RMC2:	PUSH	B	;STORE B
	PUSH	D
	PUSH	H
	MVI	B,00H	;CHANNEL A
	LXI	H,RMCRE	;RETURN ADDRESS
	PUSH	H
	LHLD	1
	LXI	D,SIOINP
	DAD	D
	PCHL
RMCRE:	POP	H
	POP	D
	POP	B
	ANI	7FH	;STRIP PARITY
	RET
	ENDIF
;
	IF	CPMDUM
RMC:
RMC2:	PUSH H ! PUSH D ! PUSH B
	LDA	ORGIO	;GET ENTRY I/OBYTE
	ANI	0FCH	;SAVE ALL BITS EXCEPT 0 & 1
	ADI	ALTCON	;SET BITS 0 & 1 TO 'CRT'
	MOV	E,A
	MVI	C,IOSET ;SET NEW I/OBYTE
	CALL	BDOS
	CALL	RCC	;INPUT CHAR FROM MODEM
	PUSH	PSW
	LDA	ORGIO	;RESTORE ENTRY I/OBYTE
	MOV	E,A
	MVI	C,IOSET ;SET I/OBYTE
	CALL	BDOS
	POP	PSW
	ANI	7FH
	POP B ! POP D ! POP H
	RET
	ENDIF
;
;
;	 GFN - GET FILE NAME 
;
GFN:	LXI	H,GFNSD	;PRINT 'WHICH DRIVE?'
	CALL	WCS
	CALL	RCC	;GET ANSWER FROM CONSOLE
	CALL	WCC	;ECHO IT TO CONSOLE
	ANI	5FH	;MAKE UPPER CASE
Š	SUI	'A'-1
	JC	GFN	;REQUIRE ALPHABETIC
	JZ	GFN
	CPI	17	;ALLOW 16 DRIVES (AS IN CP/M 2.X)
	JNC	GFN
	STA	FCB
GFNB:	LXI	H,GFNS1	;PRINT 'FILENAME? ' 
	CALL	WCS
	CALL	RCS	;READ RESPONSE INTO IBUF
	LXI	H,FCB+FN  ;BLANK FILL FN AND FT FIELDS
	MVI	C,11 
GFN1:	MVI	M,' '
	INX	H
	DCR	C
	JNZ	GFN1 
	LXI	H,IBUF	;POINT TO INPUT BUFFER
	LXI	D,FCB+FN  ;SCAN OFF FN FIELD
	MVI	C,9
GFN2:	MOV	A,M	;FETCH NEXT CHAR FROM IBUF
	INX	H
	CPI	61H	;IF LC, CONVERT TO UC 
	JC	GFN2A
	SUI	20H
GFN2A:	CPI	CR	;JUMP IF END OF LINE
	JZ	GFN5 
	CPI	'.'	;JUMP IF END OF NAME
	JZ	GFN3 
	STAX	D	;ELSE STORE CHAR IN FN FIELD
	INX	D
	DCR	C	;LOOP IF 8 OR LESS CHARS SO FAR
	JNZ	GFN2 
	JMP	GFN6	;ELSE TAKE ERROR EXIT 
;
GFN3:	LXI	D,FCB+FT  ;SCAN OFF FT FIELD
	MVI	C,4
GFN4:	MOV	A,M	;FETCH NEXT CHAR FROM IBUF
	INX	H
	CPI	61H	;IF LC, CONVERT TO UC 
	JC	GFN4A
	SUI	20H
GFN4A:	CPI	CR	;JUMP IF END OF LINE
	JZ	GFN5 
	STAX	D	;ELSE STORE CHAR IN FT FIELD
	INX	D
	DCR	C	;LOOP IF 3 OR LESS CHARS SO FAR
	JNZ	GFN4 
	JMP	GFN6	;ELSE TAKE ERROR EXIT 
;
GFN5:	XRA	A
	STA	FCB+EX	;SET EXTENT NUMBER TO ZERO
	STA	FCB+NR	;SET RECORD NUMBER TO ZERO
	STC		;CLEAR ERROR FLAG AND RETURN
	CMC 
	RET 
;
ŠGFN6:	STC		;SET ERROR FLAG AND RETURN
	RET 
;
GFNSD:	DB	CR,LF,'WHICH DRIVE? ',0
GFNS1:	DB	CR,LF,'FILENAME? ',0 
;
;	 OPEN - OPEN DISK FILE 
;
OPEN:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,OFFC 
	CALL	BDOS 
	POP	B
	POP	D
	POP	H
	RET 
;
;	 READ - READ RECORD FROM DISK FILE 
;
READ:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,RRFC 
	CALL	BDOS 
	POP	B
	POP	D
	POP	H
	RET 
;
;	 CLOSE - CLOSE DISK FILE 
;
CLOSE:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,CFFC 
	CALL	BDOS 
	POP	B
	POP	D
	POP	H
	RET 
;
;	 DELT - DELETE DISK FILE 
;
DELT:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,DFFC 
	CALL	BDOS 
	POP	B
	POP	D
Š	POP	H
	RET 
;
;	 WRITE - WRITE RECORD TO DISK
;
WRITE:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,WRFC 
	CALL	BDOS 
	POP	B
	POP	D
	POP	H
	RET 
;
;	 MAKE - MAKE NEW DISK FILE 
;
MAKE:	PUSH	H
	PUSH	D
	PUSH	B
	LXI	D,FCB
	MVI	C,MFFC
	CALL	BDOS 
	POP	B
	POP	D
	POP	H
	RET 
;
;	CITEST - CHECK CONSOLE INPUT STATUS
;
CITEST:	PUSH	B
	PUSH	D
	PUSH	H
CITCAL:	CALL	$-$	;MODIFIED BY INIT.
	ORA	A	;SET ZERO FLAG
	POP	H
	POP	D
	POP	B
	RET		;ZERO FLAG CARRIES ANSWER
;
;	MITEST - CHECK MODEM INPUT STATUS
;
	IF	PORT
MITEST:	IN	MODS	;GET MODEM UART STATUS
	XRI	MXOR	;INVERT HIGH-TRUE BITS
	ANI	MQRDA	;ANY DATA AVAILABLE?
	MVI	A,0
	JNZ	MITST1
	CMA
MITST1:	ORA	A
	RET		;ZERO FLAG CARRIES ANSWER
	ENDIF
;
;
Š	IF	TRSPT
;
MITEST:	PUSH	B
	PUSH	H
	PUSH	D
	MVI	B,00	;CHANNEL A
	LXI	H,MITSTR
	PUSH	H
	LHLD	1
	LXI	D,SIOTST
	DAD	D
	PCHL
MITSTR:	POP	D
	POP	H
	ANI	01	;TX EMPTY
	POP	B
	RET		;ZERO FLAG HOLDS THE ANSWER
	ENDIF
;
	IF CPMDUM
MITEST:	PUSH H ! PUSH D ! PUSH B
	LDA	ORGIO	;GET ENTRY I/OBYTE
	ANI	0FCH	;SAVE ALL BITS EXCEPT 0 & 1
	ADI	ALTCON	;SETS BITS 0 & 1 TO 'CRT'
	MOV	E,A
	MVI	C,IOSET ;SET NEW I/OBYTE
	CALL	BDOS
	CALL	CITEST	;GET INPUT STATUS
	PUSH	PSW	;SAVE IT
	LDA	ORGIO	;RESTORE ENTRY I/OBYTE
	MOV	E,A
	MVI	C,IOSET ;SET I/OBYTE
	CALL	BDOS
	POP	PSW	;INPUT STATUS
	POP B ! POP D ! POP H
	RET
	ENDIF
;
;	MOTEST - CHECK MODEM OUTPUT STATUS
;
;
	IF	PORT
MOTEST:	IN	MODS	;GET MODEM UART STATUS
	XRI	MXOR	;INVERT HIGH-TRUE BITS
	ANI	MTBE	;UART READY FOR CHARACTER?
	MVI	A,0
	JNZ	MOTST1	;ZERO FLAG CARRIES ANSWER
	CMA
MOTST1:	ORA	A	;SET ZERO FLAG IF READY
	RET
	ENDIF
;
	IF	TRSPT
MOTEST:	PUSH	B
	PUSH	H
Š	PUSH	D
	MVI	B,00	;CHANNEL A
	LXI	H,MOTSTR
	PUSH	H
	LHLD	1
	LXI	D,SIOTST
	DAD	D
	PCHL
MOTSTR:	ANI	02	;BUFFER EMPTY
	POP	D
	POP	H
	POP	B
	RET
	ENDIF
;
	IF	CPMDUM
MOTEST:	MVI	A,0	;ALWAYS REPORT 'CRT' (MODEM)
			;OUTPUT STATUS AS READY.
			;*** MAY NOT WORK IN ALL SYSTEMS ***
	CMA
	ORA	A
	RET
	ENDIF
;
;	 DATA AREA 
;
INCH:	DS	1	;INPUT CHAR BUFFER (TO CYBER) 
OUTCH:	DS	1	;OUTPUT CHAR BUFFER (FROM CIBER)
STACK:	DS	80	;LOCAL STACK
IBUF:	DS	256	;INPUT BUFFER 
ORGIO:	DS	1	;ENTRY I/OBYTE BUFFER (USED WITH
			;EXTERNAL MODEM ON CP/M 'CRT' DEVICE)
;
;	 TEXT BUFFER 
;
FLAG:	DS	1	;TEXT SAVE FLAG 
PTR:	DS	2	;TEXT BUFFER POINTER
SIZE:	DS	2	;TEXT BUFFER SIZE 
TBUF:	EQU	$	;START OF TEXT BUFFER 
;
	END	LINK
;

